2377 lines
119 KiB
C++
2377 lines
119 KiB
C++
#include <gtest/gtest.h>
|
|
|
|
#include <XCEngine/Components/CameraComponent.h>
|
|
#include <XCEngine/Components/LightComponent.h>
|
|
#include <XCEngine/Components/MeshFilterComponent.h>
|
|
#include <XCEngine/Components/MeshRendererComponent.h>
|
|
#include <XCEngine/Debug/ILogSink.h>
|
|
#include <XCEngine/Debug/Logger.h>
|
|
#include <XCEngine/Core/Math/Vector2.h>
|
|
#include <XCEngine/Core/Math/Vector3.h>
|
|
#include <XCEngine/Core/Math/Vector4.h>
|
|
#include <XCEngine/Input/InputManager.h>
|
|
#include <XCEngine/Input/InputTypes.h>
|
|
#include <XCEngine/Scene/Scene.h>
|
|
#include <XCEngine/Scripting/Mono/MonoScriptRuntime.h>
|
|
#include <XCEngine/Scripting/ScriptComponent.h>
|
|
#include <XCEngine/Scripting/ScriptEngine.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
using namespace XCEngine::Components;
|
|
using namespace XCEngine::Scripting;
|
|
|
|
namespace {
|
|
|
|
void ExpectVector3Near(const XCEngine::Math::Vector3& actual, const XCEngine::Math::Vector3& expected, float tolerance = 0.001f) {
|
|
EXPECT_NEAR(actual.x, expected.x, tolerance);
|
|
EXPECT_NEAR(actual.y, expected.y, tolerance);
|
|
EXPECT_NEAR(actual.z, expected.z, tolerance);
|
|
}
|
|
|
|
class CapturingLogSink final : public XCEngine::Debug::ILogSink {
|
|
public:
|
|
void Log(const XCEngine::Debug::LogEntry& entry) override {
|
|
entries.push_back(entry);
|
|
}
|
|
|
|
void Flush() override {
|
|
}
|
|
|
|
std::vector<std::string> CollectMessagesWithPrefix(const char* prefix) const {
|
|
std::vector<std::string> messages;
|
|
for (const XCEngine::Debug::LogEntry& entry : entries) {
|
|
const std::string message = entry.message.CStr();
|
|
if (message.rfind(prefix, 0) == 0) {
|
|
messages.push_back(message);
|
|
}
|
|
}
|
|
|
|
return messages;
|
|
}
|
|
|
|
std::vector<XCEngine::Debug::LogEntry> entries;
|
|
};
|
|
|
|
ScriptComponent* FindScriptComponentByClass(GameObject* gameObject, const std::string& namespaceName, const std::string& className) {
|
|
if (!gameObject) {
|
|
return nullptr;
|
|
}
|
|
|
|
for (ScriptComponent* component : gameObject->GetComponents<ScriptComponent>()) {
|
|
if (!component) {
|
|
continue;
|
|
}
|
|
|
|
if (component->GetNamespaceName() == namespaceName && component->GetClassName() == className) {
|
|
return component;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
MonoScriptRuntime::Settings CreateMonoSettings() {
|
|
MonoScriptRuntime::Settings settings;
|
|
settings.assemblyDirectory = XCENGINE_TEST_MANAGED_OUTPUT_DIR;
|
|
settings.corlibDirectory = XCENGINE_TEST_MANAGED_OUTPUT_DIR;
|
|
settings.coreAssemblyPath = XCENGINE_TEST_SCRIPT_CORE_DLL;
|
|
settings.appAssemblyPath = XCENGINE_TEST_GAME_SCRIPTS_DLL;
|
|
return settings;
|
|
}
|
|
|
|
class MonoScriptRuntimeTest : public ::testing::Test {
|
|
protected:
|
|
void SetUp() override {
|
|
engine = &ScriptEngine::Get();
|
|
engine->OnRuntimeStop();
|
|
engine->SetRuntimeFixedDeltaTime(ScriptEngine::DefaultFixedDeltaTime);
|
|
XCEngine::Input::InputManager::Get().Shutdown();
|
|
|
|
runtime = std::make_unique<MonoScriptRuntime>(CreateMonoSettings());
|
|
ASSERT_TRUE(runtime->Initialize()) << runtime->GetLastError();
|
|
|
|
engine->SetRuntime(runtime.get());
|
|
}
|
|
|
|
void TearDown() override {
|
|
engine->OnRuntimeStop();
|
|
engine->SetRuntime(nullptr);
|
|
XCEngine::Input::InputManager::Get().Shutdown();
|
|
runtime.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;
|
|
}
|
|
|
|
ScriptEngine* engine = nullptr;
|
|
std::unique_ptr<MonoScriptRuntime> runtime;
|
|
std::unique_ptr<Scene> scene;
|
|
};
|
|
|
|
TEST_F(MonoScriptRuntimeTest, InitializesAndDiscoversConcreteMonoBehaviourClasses) {
|
|
const std::vector<std::string> classNames = runtime->GetScriptClassNames("GameScripts");
|
|
|
|
EXPECT_TRUE(runtime->IsInitialized());
|
|
EXPECT_TRUE(runtime->IsClassAvailable("GameScripts", "Gameplay", "LifecycleProbe"));
|
|
EXPECT_NE(std::find(classNames.begin(), classNames.end(), "Gameplay.LifecycleProbe"), classNames.end());
|
|
EXPECT_EQ(std::find(classNames.begin(), classNames.end(), "Gameplay.AbstractLifecycleProbe"), classNames.end());
|
|
EXPECT_EQ(std::find(classNames.begin(), classNames.end(), "Gameplay.UtilityHelper"), classNames.end());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptClassDescriptorApiReturnsConcreteManagedTypes) {
|
|
std::vector<ScriptClassDescriptor> classes;
|
|
ASSERT_TRUE(runtime->TryGetAvailableScriptClasses(classes));
|
|
ASSERT_FALSE(classes.empty());
|
|
|
|
EXPECT_TRUE(std::is_sorted(
|
|
classes.begin(),
|
|
classes.end(),
|
|
[](const ScriptClassDescriptor& lhs, const ScriptClassDescriptor& rhs) {
|
|
if (lhs.assemblyName != rhs.assemblyName) {
|
|
return lhs.assemblyName < rhs.assemblyName;
|
|
}
|
|
if (lhs.namespaceName != rhs.namespaceName) {
|
|
return lhs.namespaceName < rhs.namespaceName;
|
|
}
|
|
return lhs.className < rhs.className;
|
|
}));
|
|
|
|
EXPECT_NE(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{"GameScripts", "Gameplay", "LifecycleProbe"}),
|
|
classes.end());
|
|
EXPECT_EQ(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{"GameScripts", "Gameplay", "AbstractLifecycleProbe"}),
|
|
classes.end());
|
|
EXPECT_EQ(
|
|
std::find(
|
|
classes.begin(),
|
|
classes.end(),
|
|
ScriptClassDescriptor{"GameScripts", "Gameplay", "UtilityHelper"}),
|
|
classes.end());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldMetadataListsSupportedPublicInstanceFields) {
|
|
std::vector<ScriptFieldMetadata> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldMetadata("GameScripts", "Gameplay", "FieldMetadataProbe", fields));
|
|
|
|
const std::vector<ScriptFieldMetadata> expected = {
|
|
{"Health", ScriptFieldType::Int32},
|
|
{"Label", ScriptFieldType::String},
|
|
{"SpawnPoint", ScriptFieldType::Vector3},
|
|
{"Speed", ScriptFieldType::Float},
|
|
{"Target", ScriptFieldType::GameObject},
|
|
};
|
|
|
|
EXPECT_EQ(fields, expected);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldMetadataQueryFailsForUnknownClass) {
|
|
std::vector<ScriptFieldMetadata> fields = {
|
|
{"Sentinel", ScriptFieldType::Bool},
|
|
};
|
|
|
|
EXPECT_FALSE(runtime->TryGetClassFieldMetadata("GameScripts", "Gameplay", "MissingProbe", fields));
|
|
EXPECT_TRUE(fields.empty());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ClassFieldDefaultValueQueryReturnsManagedInitializers) {
|
|
std::vector<ScriptFieldDefaultValue> fields;
|
|
|
|
EXPECT_TRUE(runtime->TryGetClassFieldDefaultValues("GameScripts", "Gameplay", "RuntimeGameObjectProbe", fields));
|
|
|
|
const auto fieldIt = std::find_if(
|
|
fields.begin(),
|
|
fields.end(),
|
|
[](const ScriptFieldDefaultValue& field) {
|
|
return field.fieldName == "ObservedRootChildCountAfterDestroy";
|
|
});
|
|
|
|
ASSERT_NE(fieldIt, fields.end());
|
|
EXPECT_EQ(fieldIt->type, ScriptFieldType::Int32);
|
|
EXPECT_EQ(std::get<int32_t>(fieldIt->value), -1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineAppliesStoredFieldsAndInvokesLifecycleMethods) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("Speed", 5.0f);
|
|
component->GetFieldStorage().SetFieldValue("Label", "Configured");
|
|
component->GetFieldStorage().SetFieldValue("Target", GameObjectReference{target->GetUUID()});
|
|
component->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 1u);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t enableCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t fixedUpdateCount = 0;
|
|
int32_t updateCount = 0;
|
|
int32_t lateUpdateCount = 0;
|
|
bool wasAwakened = false;
|
|
bool warningLogged = false;
|
|
bool errorLogged = false;
|
|
bool hasTransform = false;
|
|
bool transformLookupSucceeded = false;
|
|
bool hasUnsupportedComponent = true;
|
|
bool unsupportedComponentLookupReturnedNull = false;
|
|
bool targetResolved = false;
|
|
bool rotationAccessed = false;
|
|
bool scaleAccessed = false;
|
|
bool transformAccessed = false;
|
|
bool observedEnabled = false;
|
|
bool observedActiveSelf = false;
|
|
bool observedActiveInHierarchy = false;
|
|
bool observedIsActiveAndEnabled = false;
|
|
float speed = 0.0f;
|
|
float observedFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTimeInUpdate = 0.0f;
|
|
float observedUpdateDeltaTime = 0.0f;
|
|
float observedLateDeltaTime = 0.0f;
|
|
std::string label;
|
|
std::string observedGameObjectName;
|
|
std::string observedTargetName;
|
|
GameObjectReference targetReference;
|
|
GameObjectReference selfReference;
|
|
XCEngine::Math::Vector4 observedLocalRotation;
|
|
XCEngine::Math::Vector3 observedLocalPosition;
|
|
XCEngine::Math::Vector3 observedLocalScale;
|
|
XCEngine::Math::Vector3 spawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FixedUpdateCount", fixedUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WasAwakened", wasAwakened));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WarningLogged", warningLogged));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ErrorLogged", errorLogged));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTransform", hasTransform));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformLookupSucceeded", transformLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasUnsupportedComponent", hasUnsupportedComponent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UnsupportedComponentLookupReturnedNull", unsupportedComponentLookupReturnedNull));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "RotationAccessed", rotationAccessed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ScaleAccessed", scaleAccessed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformAccessed", transformAccessed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", speed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFixedDeltaTime", observedFixedDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTime", observedConfiguredFixedDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTimeInUpdate", observedConfiguredFixedDeltaTimeInUpdate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdateDeltaTime", observedUpdateDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLateDeltaTime", observedLateDeltaTime));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", label));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectName", observedGameObjectName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Target", targetReference));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SelfReference", selfReference));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalRotation", observedLocalRotation));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalPosition", observedLocalPosition));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalScale", observedLocalScale));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", spawnPoint));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(enableCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(fixedUpdateCount, 1);
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(lateUpdateCount, 1);
|
|
EXPECT_TRUE(wasAwakened);
|
|
EXPECT_TRUE(warningLogged);
|
|
EXPECT_TRUE(errorLogged);
|
|
EXPECT_TRUE(hasTransform);
|
|
EXPECT_TRUE(transformLookupSucceeded);
|
|
EXPECT_FALSE(hasUnsupportedComponent);
|
|
EXPECT_TRUE(unsupportedComponentLookupReturnedNull);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_TRUE(rotationAccessed);
|
|
EXPECT_TRUE(scaleAccessed);
|
|
EXPECT_TRUE(transformAccessed);
|
|
EXPECT_TRUE(observedEnabled);
|
|
EXPECT_TRUE(observedActiveSelf);
|
|
EXPECT_TRUE(observedActiveInHierarchy);
|
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
|
EXPECT_FLOAT_EQ(speed, 6.0f);
|
|
EXPECT_FLOAT_EQ(observedFixedDeltaTime, 0.02f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTime, 0.02f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTimeInUpdate, 0.02f);
|
|
EXPECT_FLOAT_EQ(observedUpdateDeltaTime, 0.016f);
|
|
EXPECT_FLOAT_EQ(observedLateDeltaTime, 0.016f);
|
|
EXPECT_EQ(label, "Configured|Awake");
|
|
EXPECT_EQ(observedGameObjectName, "Host_Managed");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(targetReference, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(selfReference, GameObjectReference{host->GetUUID()});
|
|
EXPECT_EQ(observedLocalRotation, XCEngine::Math::Vector4(0.0f, 0.5f, 0.0f, 0.8660254f));
|
|
EXPECT_EQ(observedLocalPosition, XCEngine::Math::Vector3(8.0f, 8.0f, 9.0f));
|
|
EXPECT_EQ(observedLocalScale, XCEngine::Math::Vector3(3.0f, 3.0f, 4.0f));
|
|
EXPECT_EQ(spawnPoint, XCEngine::Math::Vector3(3.0f, 4.0f, 6.0f));
|
|
EXPECT_EQ(host->GetName(), "Host_Managed");
|
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(8.0f, 8.0f, 9.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetLocalScale(), XCEngine::Math::Vector3(3.0f, 3.0f, 4.0f));
|
|
|
|
const XCEngine::Math::Quaternion& localRotation = host->GetTransform()->GetLocalRotation();
|
|
EXPECT_FLOAT_EQ(localRotation.x, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.y, 0.5f);
|
|
EXPECT_FLOAT_EQ(localRotation.z, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.w, 0.8660254f);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TimeFixedDeltaTimeUsesConfiguredRuntimeStepAcrossFixedAndVariableUpdates) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->SetRuntimeFixedDeltaTime(0.05f);
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.05f);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
float observedFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTime = 0.0f;
|
|
float observedConfiguredFixedDeltaTimeInUpdate = 0.0f;
|
|
float observedUpdateDeltaTime = 0.0f;
|
|
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedFixedDeltaTime", observedFixedDeltaTime));
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTime", observedConfiguredFixedDeltaTime));
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedConfiguredFixedDeltaTimeInUpdate", observedConfiguredFixedDeltaTimeInUpdate));
|
|
ASSERT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdateDeltaTime", observedUpdateDeltaTime));
|
|
|
|
EXPECT_FLOAT_EQ(observedFixedDeltaTime, 0.05f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTime, 0.05f);
|
|
EXPECT_FLOAT_EQ(observedConfiguredFixedDeltaTimeInUpdate, 0.05f);
|
|
EXPECT_FLOAT_EQ(observedUpdateDeltaTime, 0.016f);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedInputApiReadsCurrentNativeInputManagerState) {
|
|
XCEngine::Input::InputManager& inputManager = XCEngine::Input::InputManager::Get();
|
|
inputManager.Initialize(nullptr);
|
|
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "InputProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
inputManager.ProcessKeyDown(XCEngine::Input::KeyCode::A, false, false, false, false, false);
|
|
inputManager.ProcessKeyDown(XCEngine::Input::KeyCode::Space, false, false, false, false, false);
|
|
inputManager.ProcessKeyDown(XCEngine::Input::KeyCode::LeftCtrl, false, false, false, false, false);
|
|
inputManager.ProcessMouseButton(XCEngine::Input::MouseButton::Left, true, 120, 48);
|
|
inputManager.ProcessMouseMove(120, 48, 3, -2);
|
|
inputManager.ProcessMouseWheel(1.0f, 120, 48);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t updateCount = 0;
|
|
bool observedKeyA = false;
|
|
bool observedKeyADown = false;
|
|
bool observedKeyAUp = false;
|
|
bool observedKeySpace = false;
|
|
bool observedJump = false;
|
|
bool observedJumpDown = false;
|
|
bool observedJumpUp = false;
|
|
bool observedFire1 = false;
|
|
bool observedFire1Down = false;
|
|
bool observedFire1Up = false;
|
|
bool observedAnyKey = false;
|
|
bool observedAnyKeyDown = false;
|
|
bool observedLeftMouse = false;
|
|
bool observedLeftMouseDown = false;
|
|
bool observedLeftMouseUp = false;
|
|
float observedHorizontal = 0.0f;
|
|
float observedHorizontalRaw = 0.0f;
|
|
XCEngine::Math::Vector2 observedMouseScrollDelta;
|
|
XCEngine::Math::Vector3 observedMousePosition;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyA", observedKeyA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyADown", observedKeyADown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyAUp", observedKeyAUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeySpace", observedKeySpace));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpDown", observedJumpDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1", observedFire1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Down", observedFire1Down));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Up", observedFire1Up));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouse", observedLeftMouse));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseDown", observedLeftMouseDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseUp", observedLeftMouseUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontal", observedHorizontal));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontalRaw", observedHorizontalRaw));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMousePosition", observedMousePosition));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMouseScrollDelta", observedMouseScrollDelta));
|
|
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_TRUE(observedKeyA);
|
|
EXPECT_TRUE(observedKeyADown);
|
|
EXPECT_FALSE(observedKeyAUp);
|
|
EXPECT_TRUE(observedKeySpace);
|
|
EXPECT_TRUE(observedJump);
|
|
EXPECT_TRUE(observedJumpDown);
|
|
EXPECT_FALSE(observedJumpUp);
|
|
EXPECT_TRUE(observedFire1);
|
|
EXPECT_TRUE(observedFire1Down);
|
|
EXPECT_FALSE(observedFire1Up);
|
|
EXPECT_TRUE(observedAnyKey);
|
|
EXPECT_TRUE(observedAnyKeyDown);
|
|
EXPECT_TRUE(observedLeftMouse);
|
|
EXPECT_TRUE(observedLeftMouseDown);
|
|
EXPECT_FALSE(observedLeftMouseUp);
|
|
EXPECT_FLOAT_EQ(observedHorizontal, -1.0f);
|
|
EXPECT_FLOAT_EQ(observedHorizontalRaw, -1.0f);
|
|
EXPECT_EQ(observedMousePosition, XCEngine::Math::Vector3(120.0f, 48.0f, 0.0f));
|
|
EXPECT_EQ(observedMouseScrollDelta, XCEngine::Math::Vector2(0.0f, 1.0f));
|
|
|
|
inputManager.Update(0.016f);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyA", observedKeyA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyADown", observedKeyADown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyAUp", observedKeyAUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpDown", observedJumpDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1", observedFire1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Down", observedFire1Down));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Up", observedFire1Up));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouse", observedLeftMouse));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseDown", observedLeftMouseDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseUp", observedLeftMouseUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontalRaw", observedHorizontalRaw));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMouseScrollDelta", observedMouseScrollDelta));
|
|
|
|
EXPECT_EQ(updateCount, 2);
|
|
EXPECT_TRUE(observedKeyA);
|
|
EXPECT_FALSE(observedKeyADown);
|
|
EXPECT_FALSE(observedKeyAUp);
|
|
EXPECT_TRUE(observedJump);
|
|
EXPECT_FALSE(observedJumpDown);
|
|
EXPECT_FALSE(observedJumpUp);
|
|
EXPECT_TRUE(observedFire1);
|
|
EXPECT_FALSE(observedFire1Down);
|
|
EXPECT_FALSE(observedFire1Up);
|
|
EXPECT_TRUE(observedAnyKey);
|
|
EXPECT_FALSE(observedAnyKeyDown);
|
|
EXPECT_TRUE(observedLeftMouse);
|
|
EXPECT_FALSE(observedLeftMouseDown);
|
|
EXPECT_FALSE(observedLeftMouseUp);
|
|
EXPECT_FLOAT_EQ(observedHorizontalRaw, -1.0f);
|
|
EXPECT_EQ(observedMouseScrollDelta, XCEngine::Math::Vector2(0.0f, 0.0f));
|
|
|
|
inputManager.ProcessKeyUp(XCEngine::Input::KeyCode::A, false, false, false, false);
|
|
inputManager.ProcessKeyUp(XCEngine::Input::KeyCode::LeftCtrl, false, false, false, false);
|
|
inputManager.ProcessMouseButton(XCEngine::Input::MouseButton::Left, false, 120, 48);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyA", observedKeyA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeyAUp", observedKeyAUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1", observedFire1));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFire1Up", observedFire1Up));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouse", observedLeftMouse));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLeftMouseUp", observedLeftMouseUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontal", observedHorizontal));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedHorizontalRaw", observedHorizontalRaw));
|
|
|
|
EXPECT_EQ(updateCount, 3);
|
|
EXPECT_FALSE(observedKeyA);
|
|
EXPECT_TRUE(observedKeyAUp);
|
|
EXPECT_TRUE(observedJump);
|
|
EXPECT_FALSE(observedJumpUp);
|
|
EXPECT_FALSE(observedFire1);
|
|
EXPECT_TRUE(observedFire1Up);
|
|
EXPECT_TRUE(observedAnyKey);
|
|
EXPECT_FALSE(observedAnyKeyDown);
|
|
EXPECT_FALSE(observedLeftMouse);
|
|
EXPECT_TRUE(observedLeftMouseUp);
|
|
EXPECT_FLOAT_EQ(observedHorizontal, 0.0f);
|
|
EXPECT_FLOAT_EQ(observedHorizontalRaw, 0.0f);
|
|
|
|
inputManager.Update(0.016f);
|
|
inputManager.ProcessKeyUp(XCEngine::Input::KeyCode::Space, false, false, false, false);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedKeySpace", observedKeySpace));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJump", observedJump));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedJumpUp", observedJumpUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKey", observedAnyKey));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedAnyKeyDown", observedAnyKeyDown));
|
|
|
|
EXPECT_EQ(updateCount, 4);
|
|
EXPECT_FALSE(observedKeySpace);
|
|
EXPECT_FALSE(observedJump);
|
|
EXPECT_TRUE(observedJumpUp);
|
|
EXPECT_FALSE(observedAnyKey);
|
|
EXPECT_FALSE(observedAnyKeyDown);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedDebugLogBridgeWritesLifecycleTickMessagesToNativeLogger) {
|
|
auto sink = std::make_unique<CapturingLogSink>();
|
|
CapturingLogSink* sinkPtr = sink.get();
|
|
XCEngine::Debug::Logger::Get().AddSink(std::move(sink));
|
|
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TickLogProbe");
|
|
ASSERT_NE(component, nullptr);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
const std::vector<std::string> messages = sinkPtr->CollectMessagesWithPrefix("[TickLogProbe]");
|
|
const std::vector<std::string> expected = {
|
|
"[TickLogProbe] Awake",
|
|
"[TickLogProbe] FixedUpdate 1",
|
|
"[TickLogProbe] Start",
|
|
"[TickLogProbe] Update 1",
|
|
"[TickLogProbe] LateUpdate 1",
|
|
};
|
|
|
|
EXPECT_EQ(messages, expected);
|
|
|
|
XCEngine::Debug::Logger::Get().RemoveSink(sinkPtr);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, DeserializedSceneRebindsManagedScriptsAndRestoresStoredFields) {
|
|
Scene originalScene("SerializedMonoScene");
|
|
GameObject* hostA = originalScene.CreateGameObject("HostA");
|
|
GameObject* hostB = originalScene.CreateGameObject("HostB");
|
|
GameObject* targetA = originalScene.CreateGameObject("TargetA");
|
|
GameObject* targetB = originalScene.CreateGameObject("TargetB");
|
|
|
|
ScriptComponent* originalComponentA = AddScript(hostA, "Gameplay", "LifecycleProbe");
|
|
ScriptComponent* originalComponentB = AddScript(hostB, "Gameplay", "LifecycleProbe");
|
|
|
|
originalComponentA->GetFieldStorage().SetFieldValue("Speed", 2.5f);
|
|
originalComponentA->GetFieldStorage().SetFieldValue("Label", "Alpha");
|
|
originalComponentA->GetFieldStorage().SetFieldValue("Target", GameObjectReference{targetA->GetUUID()});
|
|
originalComponentA->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
|
|
originalComponentB->GetFieldStorage().SetFieldValue("Speed", 9.0f);
|
|
originalComponentB->GetFieldStorage().SetFieldValue("Label", "Beta");
|
|
originalComponentB->GetFieldStorage().SetFieldValue("Target", GameObjectReference{targetB->GetUUID()});
|
|
originalComponentB->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(4.0f, 5.0f, 6.0f));
|
|
|
|
const uint64_t originalUUIDA = originalComponentA->GetScriptComponentUUID();
|
|
const uint64_t originalUUIDB = originalComponentB->GetScriptComponentUUID();
|
|
const std::string serializedScene = originalScene.SerializeToString();
|
|
|
|
scene = std::make_unique<Scene>("LoadedMonoScene");
|
|
scene->DeserializeFromString(serializedScene);
|
|
Scene* runtimeScene = scene.get();
|
|
|
|
GameObject* loadedHostA = runtimeScene->Find("HostA");
|
|
GameObject* loadedHostB = runtimeScene->Find("HostB");
|
|
GameObject* loadedTargetA = runtimeScene->Find("TargetA");
|
|
GameObject* loadedTargetB = runtimeScene->Find("TargetB");
|
|
ASSERT_NE(loadedHostA, nullptr);
|
|
ASSERT_NE(loadedHostB, nullptr);
|
|
ASSERT_NE(loadedTargetA, nullptr);
|
|
ASSERT_NE(loadedTargetB, nullptr);
|
|
|
|
ScriptComponent* loadedComponentA = FindScriptComponentByClass(loadedHostA, "Gameplay", "LifecycleProbe");
|
|
ScriptComponent* loadedComponentB = FindScriptComponentByClass(loadedHostB, "Gameplay", "LifecycleProbe");
|
|
ASSERT_NE(loadedComponentA, nullptr);
|
|
ASSERT_NE(loadedComponentB, nullptr);
|
|
|
|
EXPECT_EQ(loadedComponentA->GetScriptComponentUUID(), originalUUIDA);
|
|
EXPECT_EQ(loadedComponentB->GetScriptComponentUUID(), originalUUIDB);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(loadedComponentA));
|
|
EXPECT_TRUE(runtime->HasManagedInstance(loadedComponentB));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 2u);
|
|
|
|
int32_t awakeCountA = 0;
|
|
int32_t startCountA = 0;
|
|
int32_t awakeCountB = 0;
|
|
int32_t startCountB = 0;
|
|
bool targetResolvedA = false;
|
|
bool targetResolvedB = false;
|
|
float speedA = 0.0f;
|
|
float speedB = 0.0f;
|
|
std::string labelA;
|
|
std::string labelB;
|
|
std::string observedTargetNameA;
|
|
std::string observedTargetNameB;
|
|
GameObjectReference targetReferenceA;
|
|
GameObjectReference targetReferenceB;
|
|
GameObjectReference selfReferenceA;
|
|
GameObjectReference selfReferenceB;
|
|
XCEngine::Math::Vector3 spawnPointA;
|
|
XCEngine::Math::Vector3 spawnPointB;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "AwakeCount", awakeCountA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "StartCount", startCountA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "TargetResolved", targetResolvedA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "Speed", speedA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "Label", labelA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "ObservedTargetName", observedTargetNameA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "Target", targetReferenceA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "SelfReference", selfReferenceA));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentA, "SpawnPoint", spawnPointA));
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "AwakeCount", awakeCountB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "StartCount", startCountB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "TargetResolved", targetResolvedB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "Speed", speedB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "Label", labelB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "ObservedTargetName", observedTargetNameB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "Target", targetReferenceB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "SelfReference", selfReferenceB));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponentB, "SpawnPoint", spawnPointB));
|
|
|
|
EXPECT_EQ(awakeCountA, 1);
|
|
EXPECT_EQ(startCountA, 1);
|
|
EXPECT_TRUE(targetResolvedA);
|
|
EXPECT_FLOAT_EQ(speedA, 3.5f);
|
|
EXPECT_EQ(labelA, "Alpha|Awake");
|
|
EXPECT_EQ(observedTargetNameA, "TargetA");
|
|
EXPECT_EQ(targetReferenceA, GameObjectReference{loadedTargetA->GetUUID()});
|
|
EXPECT_EQ(selfReferenceA, GameObjectReference{loadedHostA->GetUUID()});
|
|
EXPECT_EQ(spawnPointA, XCEngine::Math::Vector3(2.0f, 2.0f, 3.0f));
|
|
|
|
EXPECT_EQ(awakeCountB, 1);
|
|
EXPECT_EQ(startCountB, 1);
|
|
EXPECT_TRUE(targetResolvedB);
|
|
EXPECT_FLOAT_EQ(speedB, 10.0f);
|
|
EXPECT_EQ(labelB, "Beta|Awake");
|
|
EXPECT_EQ(observedTargetNameB, "TargetB");
|
|
EXPECT_EQ(targetReferenceB, GameObjectReference{loadedTargetB->GetUUID()});
|
|
EXPECT_EQ(selfReferenceB, GameObjectReference{loadedHostB->GetUUID()});
|
|
EXPECT_EQ(spawnPointB, XCEngine::Math::Vector3(5.0f, 5.0f, 6.0f));
|
|
|
|
EXPECT_EQ(loadedHostA->GetName(), "HostA_Managed");
|
|
EXPECT_EQ(loadedHostB->GetName(), "HostB_Managed");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedFieldChangesWriteBackToStoredCacheAndPersistAcrossSceneRoundTrip) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("Speed", 5.0f);
|
|
component->GetFieldStorage().SetFieldValue("Label", "Configured");
|
|
component->GetFieldStorage().SetFieldValue("Target", GameObjectReference{target->GetUUID()});
|
|
component->GetFieldStorage().SetFieldValue("SpawnPoint", XCEngine::Math::Vector3(2.0f, 4.0f, 6.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnFixedUpdate(0.02f);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
float storedSpeed = 0.0f;
|
|
std::string storedLabel;
|
|
GameObjectReference storedTarget;
|
|
XCEngine::Math::Vector3 storedSpawnPoint;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeed));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabel));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTarget));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPoint));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeed, 6.0f);
|
|
EXPECT_EQ(storedLabel, "Configured|Awake");
|
|
EXPECT_EQ(storedTarget, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPoint, XCEngine::Math::Vector3(3.0f, 4.0f, 6.0f));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("AwakeCount"));
|
|
|
|
const std::string persistedHostName = host->GetName();
|
|
const std::string serializedScene = runtimeScene->SerializeToString();
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
scene = std::make_unique<Scene>("ReloadedMonoScene");
|
|
scene->DeserializeFromString(serializedScene);
|
|
Scene* reloadedScene = scene.get();
|
|
|
|
GameObject* loadedHost = reloadedScene->Find(persistedHostName);
|
|
GameObject* loadedTarget = reloadedScene->Find("Target");
|
|
ASSERT_NE(loadedHost, nullptr);
|
|
ASSERT_NE(loadedTarget, nullptr);
|
|
|
|
ScriptComponent* loadedComponent = FindScriptComponentByClass(loadedHost, "Gameplay", "LifecycleProbe");
|
|
ASSERT_NE(loadedComponent, nullptr);
|
|
|
|
float loadedStoredSpeed = 0.0f;
|
|
std::string loadedStoredLabel;
|
|
GameObjectReference loadedStoredTarget;
|
|
XCEngine::Math::Vector3 loadedStoredSpawnPoint;
|
|
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Speed", loadedStoredSpeed));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Label", loadedStoredLabel));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("Target", loadedStoredTarget));
|
|
EXPECT_TRUE(loadedComponent->GetFieldStorage().TryGetFieldValue("SpawnPoint", loadedStoredSpawnPoint));
|
|
|
|
EXPECT_FLOAT_EQ(loadedStoredSpeed, 6.0f);
|
|
EXPECT_EQ(loadedStoredLabel, "Configured|Awake");
|
|
EXPECT_EQ(loadedStoredTarget, GameObjectReference{loadedTarget->GetUUID()});
|
|
EXPECT_EQ(loadedStoredSpawnPoint, XCEngine::Math::Vector3(3.0f, 4.0f, 6.0f));
|
|
EXPECT_FALSE(loadedComponent->GetFieldStorage().Contains("AwakeCount"));
|
|
|
|
engine->OnRuntimeStart(reloadedScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t updateCount = 0;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(loadedComponent, "ObservedTargetName", observedTargetName));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 7.0f);
|
|
EXPECT_EQ(runtimeLabel, "Configured|Awake|Awake");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiUpdatesLiveManagedInstanceAndStoredCache) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Speed", 41.0f));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Label", "Edited"));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Target", GameObjectReference{target->GetUUID()}));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "SpawnPoint", XCEngine::Math::Vector3(10.0f, 20.0f, 30.0f)));
|
|
|
|
EXPECT_FALSE(engine->TrySetScriptFieldValue(component, "DoesNotExist", int32_t(1)));
|
|
EXPECT_FALSE(engine->TrySetScriptFieldValue(component, "Speed", std::string("wrong")));
|
|
|
|
float storedSpeedBeforeUpdate = 0.0f;
|
|
std::string storedLabelBeforeUpdate;
|
|
GameObjectReference storedTargetBeforeUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointBeforeUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedBeforeUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelBeforeUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetBeforeUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointBeforeUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedBeforeUpdate, 41.0f);
|
|
EXPECT_EQ(storedLabelBeforeUpdate, "Edited");
|
|
EXPECT_EQ(storedTargetBeforeUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointBeforeUpdate, XCEngine::Math::Vector3(10.0f, 20.0f, 30.0f));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("DoesNotExist"));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 42.0f);
|
|
EXPECT_EQ(runtimeLabel, "Edited");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
|
|
float storedSpeedAfterUpdate = 0.0f;
|
|
std::string storedLabelAfterUpdate;
|
|
GameObjectReference storedTargetAfterUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointAfterUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointAfterUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedAfterUpdate, 42.0f);
|
|
EXPECT_EQ(storedLabelAfterUpdate, "Edited");
|
|
EXPECT_EQ(storedTargetAfterUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointAfterUpdate, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiCachesValuesBeforeRuntimeStartAndAppliesThemOnFirstInstance) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
EXPECT_FALSE(runtime->HasManagedInstance(component));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Speed", 9.0f));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Label", "PreStart"));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Target", GameObjectReference{target->GetUUID()}));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "SpawnPoint", XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f)));
|
|
|
|
float storedSpeedBeforeRuntime = 0.0f;
|
|
std::string storedLabelBeforeRuntime;
|
|
GameObjectReference storedTargetBeforeRuntime;
|
|
XCEngine::Math::Vector3 storedSpawnPointBeforeRuntime;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedBeforeRuntime));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelBeforeRuntime));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetBeforeRuntime));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointBeforeRuntime));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedBeforeRuntime, 9.0f);
|
|
EXPECT_EQ(storedLabelBeforeRuntime, "PreStart");
|
|
EXPECT_EQ(storedTargetBeforeRuntime, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointBeforeRuntime, XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 10.0f);
|
|
EXPECT_EQ(runtimeLabel, "PreStart|Awake");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(4.0f, 4.0f, 5.0f));
|
|
|
|
float storedSpeedAfterUpdate = 0.0f;
|
|
std::string storedLabelAfterUpdate;
|
|
GameObjectReference storedTargetAfterUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointAfterUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointAfterUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedAfterUpdate, 10.0f);
|
|
EXPECT_EQ(storedLabelAfterUpdate, "PreStart|Awake");
|
|
EXPECT_EQ(storedTargetAfterUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointAfterUpdate, XCEngine::Math::Vector3(4.0f, 4.0f, 5.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldApiCachesValuesForInactiveRuntimeScriptUntilActivation) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
host->SetActive(false);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
EXPECT_FALSE(runtime->HasManagedInstance(component));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Speed", 12.0f));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Label", "Dormant"));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "Target", GameObjectReference{target->GetUUID()}));
|
|
EXPECT_TRUE(engine->TrySetScriptFieldValue(component, "SpawnPoint", XCEngine::Math::Vector3(5.0f, 6.0f, 7.0f)));
|
|
|
|
float storedSpeedBeforeActivation = 0.0f;
|
|
std::string storedLabelBeforeActivation;
|
|
GameObjectReference storedTargetBeforeActivation;
|
|
XCEngine::Math::Vector3 storedSpawnPointBeforeActivation;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedBeforeActivation));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelBeforeActivation));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetBeforeActivation));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointBeforeActivation));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedBeforeActivation, 12.0f);
|
|
EXPECT_EQ(storedLabelBeforeActivation, "Dormant");
|
|
EXPECT_EQ(storedTargetBeforeActivation, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointBeforeActivation, XCEngine::Math::Vector3(5.0f, 6.0f, 7.0f));
|
|
|
|
host->SetActive(true);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 13.0f);
|
|
EXPECT_EQ(runtimeLabel, "Dormant|Awake");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(6.0f, 6.0f, 7.0f));
|
|
|
|
float storedSpeedAfterUpdate = 0.0f;
|
|
std::string storedLabelAfterUpdate;
|
|
GameObjectReference storedTargetAfterUpdate;
|
|
XCEngine::Math::Vector3 storedSpawnPointAfterUpdate;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeedAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabelAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTargetAfterUpdate));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPointAfterUpdate));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeedAfterUpdate, 13.0f);
|
|
EXPECT_EQ(storedLabelAfterUpdate, "Dormant|Awake");
|
|
EXPECT_EQ(storedTargetAfterUpdate, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPointAfterUpdate, XCEngine::Math::Vector3(6.0f, 6.0f, 7.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldBatchApiUpdatesLiveManagedInstanceAndReportsPerFieldStatus) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* target = runtimeScene->CreateGameObject("Target");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(99));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
const std::vector<ScriptFieldWriteRequest> requests = {
|
|
{"Speed", ScriptFieldType::Float, ScriptFieldValue(41.0f)},
|
|
{"Label", ScriptFieldType::String, ScriptFieldValue(std::string("BatchEdited"))},
|
|
{"Target", ScriptFieldType::GameObject, ScriptFieldValue(GameObjectReference{target->GetUUID()})},
|
|
{"SpawnPoint", ScriptFieldType::Vector3, ScriptFieldValue(XCEngine::Math::Vector3(10.0f, 20.0f, 30.0f))},
|
|
{"LegacyOnly", ScriptFieldType::UInt64, ScriptFieldValue(uint64_t(100))},
|
|
{"DoesNotExist", ScriptFieldType::Int32, ScriptFieldValue(int32_t(1))},
|
|
{"Speed", ScriptFieldType::String, ScriptFieldValue(std::string("wrong"))},
|
|
{"Label", ScriptFieldType::String, ScriptFieldValue(int32_t(5))}
|
|
};
|
|
|
|
std::vector<ScriptFieldWriteResult> results;
|
|
EXPECT_FALSE(engine->ApplyScriptFieldWrites(component, requests, results));
|
|
ASSERT_EQ(results.size(), requests.size());
|
|
|
|
EXPECT_EQ(results[0].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[1].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[2].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[3].status, ScriptFieldWriteStatus::Applied);
|
|
EXPECT_EQ(results[4].status, ScriptFieldWriteStatus::StoredOnlyField);
|
|
EXPECT_EQ(results[5].status, ScriptFieldWriteStatus::UnknownField);
|
|
EXPECT_EQ(results[6].status, ScriptFieldWriteStatus::TypeMismatch);
|
|
EXPECT_EQ(results[7].status, ScriptFieldWriteStatus::InvalidValue);
|
|
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t startCount = 0;
|
|
bool targetResolved = false;
|
|
float runtimeSpeed = 0.0f;
|
|
std::string runtimeLabel;
|
|
std::string observedTargetName;
|
|
XCEngine::Math::Vector3 runtimeSpawnPoint;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetResolved", targetResolved));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Speed", runtimeSpeed));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", runtimeLabel));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetName", observedTargetName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "SpawnPoint", runtimeSpawnPoint));
|
|
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_TRUE(targetResolved);
|
|
EXPECT_FLOAT_EQ(runtimeSpeed, 42.0f);
|
|
EXPECT_EQ(runtimeLabel, "BatchEdited");
|
|
EXPECT_EQ(observedTargetName, "Target");
|
|
EXPECT_EQ(runtimeSpawnPoint, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
|
|
float storedSpeed = 0.0f;
|
|
std::string storedLabel;
|
|
GameObjectReference storedTarget;
|
|
XCEngine::Math::Vector3 storedSpawnPoint;
|
|
uint64_t storedLegacyOnly = 0;
|
|
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Speed", storedSpeed));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Label", storedLabel));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("Target", storedTarget));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("SpawnPoint", storedSpawnPoint));
|
|
EXPECT_TRUE(component->GetFieldStorage().TryGetFieldValue("LegacyOnly", storedLegacyOnly));
|
|
|
|
EXPECT_FLOAT_EQ(storedSpeed, 42.0f);
|
|
EXPECT_EQ(storedLabel, "BatchEdited");
|
|
EXPECT_EQ(storedTarget, GameObjectReference{target->GetUUID()});
|
|
EXPECT_EQ(storedSpawnPoint, XCEngine::Math::Vector3(11.0f, 20.0f, 30.0f));
|
|
EXPECT_EQ(storedLegacyOnly, 99u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldModelUsesManagedClassDefaultValuesBeforeRuntimeStart) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "RuntimeGameObjectProbe");
|
|
|
|
ScriptFieldModel model;
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Available);
|
|
|
|
const auto fieldIt = std::find_if(
|
|
model.fields.begin(),
|
|
model.fields.end(),
|
|
[](const ScriptFieldSnapshot& field) {
|
|
return field.metadata.name == "ObservedRootChildCountAfterDestroy";
|
|
});
|
|
|
|
ASSERT_NE(fieldIt, model.fields.end());
|
|
EXPECT_TRUE(fieldIt->declaredInClass);
|
|
EXPECT_TRUE(fieldIt->hasDefaultValue);
|
|
EXPECT_FALSE(fieldIt->hasValue);
|
|
EXPECT_EQ(fieldIt->valueSource, ScriptFieldValueSource::DefaultValue);
|
|
EXPECT_EQ(std::get<int32_t>(fieldIt->defaultValue), -1);
|
|
EXPECT_EQ(std::get<int32_t>(fieldIt->value), -1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldClearApiRestoresManagedClassDefaultValuesAndRemovesStoredOverrides) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "RuntimeGameObjectProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("ObservedRootChildCountAfterDestroy", int32_t(7));
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(99));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
const std::vector<ScriptFieldClearRequest> requests = {
|
|
{"ObservedRootChildCountAfterDestroy"},
|
|
{"LegacyOnly"},
|
|
{"DoesNotExist"},
|
|
{""}
|
|
};
|
|
|
|
std::vector<ScriptFieldClearResult> results;
|
|
EXPECT_FALSE(engine->ClearScriptFieldOverrides(component, requests, results));
|
|
ASSERT_EQ(results.size(), requests.size());
|
|
|
|
EXPECT_EQ(results[0].status, ScriptFieldClearStatus::Applied);
|
|
EXPECT_EQ(results[1].status, ScriptFieldClearStatus::Applied);
|
|
EXPECT_EQ(results[2].status, ScriptFieldClearStatus::UnknownField);
|
|
EXPECT_EQ(results[3].status, ScriptFieldClearStatus::EmptyFieldName);
|
|
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("ObservedRootChildCountAfterDestroy"));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("LegacyOnly"));
|
|
|
|
int32_t runtimeObservedCountAfterDestroy = 0;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountAfterDestroy", runtimeObservedCountAfterDestroy));
|
|
EXPECT_EQ(runtimeObservedCountAfterDestroy, -1);
|
|
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("ObservedRootChildCountAfterDestroy"));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("LegacyOnly"));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldReadApiReturnsManagedOnlyRuntimeValues) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
float speed = 0.0f;
|
|
int32_t awakeCount = 0;
|
|
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("Speed"));
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("AwakeCount"));
|
|
EXPECT_FALSE(engine->TryGetScriptFieldValue(component, "Speed", speed));
|
|
EXPECT_FALSE(engine->TryGetScriptFieldValue(component, "AwakeCount", awakeCount));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
EXPECT_TRUE(engine->TryGetScriptFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("AwakeCount"));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
EXPECT_TRUE(engine->TryGetScriptFieldValue(component, "Speed", speed));
|
|
EXPECT_FLOAT_EQ(speed, 1.0f);
|
|
EXPECT_FALSE(component->GetFieldStorage().Contains("Speed"));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldSnapshotApiReportsMetadataAndCurrentValues) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("AwakeCount", uint64_t(7));
|
|
component->GetFieldStorage().SetFieldValue("Label", "Stored");
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(99));
|
|
|
|
ScriptFieldModel model;
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Available);
|
|
|
|
std::vector<ScriptFieldSnapshot>& snapshots = model.fields;
|
|
|
|
const auto findSnapshot = [&snapshots](const std::string& fieldName) -> const ScriptFieldSnapshot* {
|
|
const auto it = std::find_if(
|
|
snapshots.begin(),
|
|
snapshots.end(),
|
|
[&fieldName](const ScriptFieldSnapshot& snapshot) {
|
|
return snapshot.metadata.name == fieldName;
|
|
});
|
|
return it != snapshots.end() ? &(*it) : nullptr;
|
|
};
|
|
|
|
const ScriptFieldSnapshot* awakeSnapshot = findSnapshot("AwakeCount");
|
|
const ScriptFieldSnapshot* labelSnapshot = findSnapshot("Label");
|
|
const ScriptFieldSnapshot* speedSnapshot = findSnapshot("Speed");
|
|
const ScriptFieldSnapshot* legacySnapshot = findSnapshot("LegacyOnly");
|
|
|
|
ASSERT_NE(awakeSnapshot, nullptr);
|
|
ASSERT_NE(labelSnapshot, nullptr);
|
|
ASSERT_NE(speedSnapshot, nullptr);
|
|
ASSERT_NE(legacySnapshot, nullptr);
|
|
|
|
EXPECT_EQ(awakeSnapshot->metadata.type, ScriptFieldType::Int32);
|
|
EXPECT_TRUE(awakeSnapshot->declaredInClass);
|
|
EXPECT_TRUE(awakeSnapshot->hasDefaultValue);
|
|
EXPECT_FALSE(awakeSnapshot->hasValue);
|
|
EXPECT_EQ(awakeSnapshot->valueSource, ScriptFieldValueSource::DefaultValue);
|
|
EXPECT_EQ(awakeSnapshot->issue, ScriptFieldIssue::TypeMismatch);
|
|
EXPECT_TRUE(awakeSnapshot->hasStoredValue);
|
|
EXPECT_EQ(awakeSnapshot->storedType, ScriptFieldType::UInt64);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->defaultValue), 0);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->value), 0);
|
|
EXPECT_EQ(std::get<uint64_t>(awakeSnapshot->storedValue), 7u);
|
|
|
|
EXPECT_EQ(labelSnapshot->metadata.type, ScriptFieldType::String);
|
|
EXPECT_TRUE(labelSnapshot->declaredInClass);
|
|
EXPECT_TRUE(labelSnapshot->hasDefaultValue);
|
|
EXPECT_TRUE(labelSnapshot->hasValue);
|
|
EXPECT_EQ(labelSnapshot->valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(labelSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_TRUE(labelSnapshot->hasStoredValue);
|
|
EXPECT_EQ(labelSnapshot->storedType, ScriptFieldType::String);
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->defaultValue), "");
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->value), "Stored");
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->storedValue), "Stored");
|
|
|
|
EXPECT_EQ(speedSnapshot->metadata.type, ScriptFieldType::Float);
|
|
EXPECT_TRUE(speedSnapshot->declaredInClass);
|
|
EXPECT_TRUE(speedSnapshot->hasDefaultValue);
|
|
EXPECT_FALSE(speedSnapshot->hasValue);
|
|
EXPECT_EQ(speedSnapshot->valueSource, ScriptFieldValueSource::DefaultValue);
|
|
EXPECT_EQ(speedSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->defaultValue), 0.0f);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->value), 0.0f);
|
|
|
|
EXPECT_EQ(legacySnapshot->metadata.type, ScriptFieldType::UInt64);
|
|
EXPECT_FALSE(legacySnapshot->declaredInClass);
|
|
EXPECT_FALSE(legacySnapshot->hasDefaultValue);
|
|
EXPECT_TRUE(legacySnapshot->hasValue);
|
|
EXPECT_EQ(legacySnapshot->valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(legacySnapshot->issue, ScriptFieldIssue::StoredOnly);
|
|
EXPECT_TRUE(legacySnapshot->hasStoredValue);
|
|
EXPECT_EQ(legacySnapshot->storedType, ScriptFieldType::UInt64);
|
|
EXPECT_EQ(std::get<uint64_t>(legacySnapshot->value), 99u);
|
|
EXPECT_EQ(std::get<uint64_t>(legacySnapshot->storedValue), 99u);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Available);
|
|
awakeSnapshot = findSnapshot("AwakeCount");
|
|
labelSnapshot = findSnapshot("Label");
|
|
speedSnapshot = findSnapshot("Speed");
|
|
legacySnapshot = findSnapshot("LegacyOnly");
|
|
|
|
ASSERT_NE(awakeSnapshot, nullptr);
|
|
ASSERT_NE(labelSnapshot, nullptr);
|
|
ASSERT_NE(speedSnapshot, nullptr);
|
|
ASSERT_NE(legacySnapshot, nullptr);
|
|
|
|
EXPECT_TRUE(awakeSnapshot->hasValue);
|
|
EXPECT_TRUE(awakeSnapshot->hasDefaultValue);
|
|
EXPECT_EQ(awakeSnapshot->valueSource, ScriptFieldValueSource::ManagedValue);
|
|
EXPECT_EQ(awakeSnapshot->issue, ScriptFieldIssue::TypeMismatch);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->defaultValue), 0);
|
|
EXPECT_EQ(std::get<int32_t>(awakeSnapshot->value), 1);
|
|
EXPECT_TRUE(labelSnapshot->hasValue);
|
|
EXPECT_TRUE(labelSnapshot->hasDefaultValue);
|
|
EXPECT_EQ(labelSnapshot->valueSource, ScriptFieldValueSource::ManagedValue);
|
|
EXPECT_EQ(labelSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->defaultValue), "");
|
|
EXPECT_EQ(std::get<std::string>(labelSnapshot->value), "Stored|Awake");
|
|
EXPECT_TRUE(speedSnapshot->hasValue);
|
|
EXPECT_TRUE(speedSnapshot->hasDefaultValue);
|
|
EXPECT_EQ(speedSnapshot->valueSource, ScriptFieldValueSource::ManagedValue);
|
|
EXPECT_EQ(speedSnapshot->issue, ScriptFieldIssue::None);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->defaultValue), 0.0f);
|
|
EXPECT_FLOAT_EQ(std::get<float>(speedSnapshot->value), 1.0f);
|
|
EXPECT_TRUE(legacySnapshot->hasValue);
|
|
EXPECT_FALSE(legacySnapshot->hasDefaultValue);
|
|
EXPECT_EQ(legacySnapshot->valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(legacySnapshot->issue, ScriptFieldIssue::StoredOnly);
|
|
EXPECT_EQ(std::get<uint64_t>(legacySnapshot->value), 99u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ScriptEngineFieldModelReportsMissingScriptClassAndStoredFields) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MissingLifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("Label", "Stored");
|
|
component->GetFieldStorage().SetFieldValue("LegacyOnly", uint64_t(9));
|
|
|
|
ScriptFieldModel model;
|
|
ASSERT_TRUE(engine->TryGetScriptFieldModel(component, model));
|
|
|
|
EXPECT_EQ(model.classStatus, ScriptFieldClassStatus::Missing);
|
|
ASSERT_EQ(model.fields.size(), 2u);
|
|
EXPECT_EQ(model.fields[0].metadata.name, "Label");
|
|
EXPECT_EQ(model.fields[1].metadata.name, "LegacyOnly");
|
|
|
|
for (const ScriptFieldSnapshot& field : model.fields) {
|
|
EXPECT_FALSE(field.declaredInClass);
|
|
EXPECT_FALSE(field.hasDefaultValue);
|
|
EXPECT_TRUE(field.hasValue);
|
|
EXPECT_EQ(field.valueSource, ScriptFieldValueSource::StoredValue);
|
|
EXPECT_EQ(field.issue, ScriptFieldIssue::StoredOnly);
|
|
EXPECT_TRUE(field.hasStoredValue);
|
|
EXPECT_EQ(field.metadata.type, field.storedType);
|
|
EXPECT_EQ(field.value, field.storedValue);
|
|
}
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectComponentApiResolvesTransformAndRejectsUnsupportedManagedTypes) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasTransform = false;
|
|
bool transformLookupSucceeded = false;
|
|
bool hasUnsupportedComponent = true;
|
|
bool unsupportedComponentLookupReturnedNull = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTransform", hasTransform));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TransformLookupSucceeded", transformLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasUnsupportedComponent", hasUnsupportedComponent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UnsupportedComponentLookupReturnedNull", unsupportedComponentLookupReturnedNull));
|
|
|
|
EXPECT_TRUE(hasTransform);
|
|
EXPECT_TRUE(transformLookupSucceeded);
|
|
EXPECT_FALSE(hasUnsupportedComponent);
|
|
EXPECT_TRUE(unsupportedComponentLookupReturnedNull);
|
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(7.0f, 8.0f, 9.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedBuiltInComponentWrappersReadAndWriteCameraAndLight) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
CameraComponent* camera = host->AddComponent<CameraComponent>();
|
|
LightComponent* light = host->AddComponent<LightComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "BuiltinComponentProbe");
|
|
|
|
camera->SetFieldOfView(52.0f);
|
|
camera->SetNearClipPlane(0.2f);
|
|
camera->SetFarClipPlane(300.0f);
|
|
camera->SetDepth(1.5f);
|
|
camera->SetPrimary(true);
|
|
|
|
light->SetIntensity(1.25f);
|
|
light->SetRange(12.0f);
|
|
light->SetSpotAngle(33.0f);
|
|
light->SetCastsShadows(false);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasCamera = false;
|
|
bool hasLight = false;
|
|
bool cameraLookupSucceeded = false;
|
|
bool lightLookupSucceeded = false;
|
|
bool observedPrimary = false;
|
|
bool observedCastsShadows = true;
|
|
float observedFieldOfView = 0.0f;
|
|
float observedNearClipPlane = 0.0f;
|
|
float observedFarClipPlane = 0.0f;
|
|
float observedDepth = 0.0f;
|
|
float observedIntensity = 0.0f;
|
|
float observedRange = 0.0f;
|
|
float observedSpotAngle = 0.0f;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasCamera", hasCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasLight", hasLight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFieldOfView", observedFieldOfView));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedNearClipPlane", observedNearClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFarClipPlane", observedFarClipPlane));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedDepth", observedDepth));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPrimary", observedPrimary));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIntensity", observedIntensity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRange", observedRange));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedSpotAngle", observedSpotAngle));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastsShadows", observedCastsShadows));
|
|
|
|
EXPECT_TRUE(hasCamera);
|
|
EXPECT_TRUE(hasLight);
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_TRUE(lightLookupSucceeded);
|
|
|
|
EXPECT_FLOAT_EQ(observedFieldOfView, 52.0f);
|
|
EXPECT_FLOAT_EQ(observedNearClipPlane, 0.2f);
|
|
EXPECT_FLOAT_EQ(observedFarClipPlane, 300.0f);
|
|
EXPECT_FLOAT_EQ(observedDepth, 1.5f);
|
|
EXPECT_TRUE(observedPrimary);
|
|
EXPECT_FLOAT_EQ(observedIntensity, 1.25f);
|
|
EXPECT_FLOAT_EQ(observedRange, 12.0f);
|
|
EXPECT_FLOAT_EQ(observedSpotAngle, 33.0f);
|
|
EXPECT_FALSE(observedCastsShadows);
|
|
|
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 75.0f);
|
|
EXPECT_FLOAT_EQ(camera->GetNearClipPlane(), 0.3f);
|
|
EXPECT_FLOAT_EQ(camera->GetFarClipPlane(), 500.0f);
|
|
EXPECT_FLOAT_EQ(camera->GetDepth(), 3.0f);
|
|
EXPECT_FALSE(camera->IsPrimary());
|
|
|
|
EXPECT_FLOAT_EQ(light->GetIntensity(), 2.5f);
|
|
EXPECT_FLOAT_EQ(light->GetRange(), 42.0f);
|
|
EXPECT_FLOAT_EQ(light->GetSpotAngle(), 55.0f);
|
|
EXPECT_TRUE(light->GetCastsShadows());
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedMeshComponentWrappersReadAndWritePathsAndFlags) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
MeshFilterComponent* meshFilter = host->AddComponent<MeshFilterComponent>();
|
|
MeshRendererComponent* meshRenderer = host->AddComponent<MeshRendererComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MeshComponentProbe");
|
|
|
|
meshFilter->SetMeshPath("Meshes/initial.mesh");
|
|
meshRenderer->SetMaterialPath(0, "Materials/initial.mat");
|
|
meshRenderer->SetCastShadows(true);
|
|
meshRenderer->SetReceiveShadows(false);
|
|
meshRenderer->SetRenderLayer(7);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool hasMeshFilter = false;
|
|
bool hasMeshRenderer = false;
|
|
bool meshFilterLookupSucceeded = false;
|
|
bool meshRendererLookupSucceeded = false;
|
|
std::string observedInitialMeshPath;
|
|
std::string observedUpdatedMeshPath;
|
|
int32_t observedInitialMaterialCount = 0;
|
|
std::string observedInitialMaterial0Path;
|
|
bool observedInitialCastShadows = false;
|
|
bool observedInitialReceiveShadows = true;
|
|
int32_t observedInitialRenderLayer = 0;
|
|
int32_t observedUpdatedMaterialCount = 0;
|
|
std::string observedUpdatedMaterial1Path;
|
|
bool observedUpdatedCastShadows = true;
|
|
bool observedUpdatedReceiveShadows = false;
|
|
int32_t observedUpdatedRenderLayer = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshFilter", hasMeshFilter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshRenderer", hasMeshRenderer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshFilterLookupSucceeded", meshFilterLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMeshPath", observedInitialMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMeshPath", observedUpdatedMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterialCount", observedInitialMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterial0Path", observedInitialMaterial0Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialCastShadows", observedInitialCastShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialReceiveShadows", observedInitialReceiveShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialRenderLayer", observedInitialRenderLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMaterialCount", observedUpdatedMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedMaterial1Path", observedUpdatedMaterial1Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCastShadows", observedUpdatedCastShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedReceiveShadows", observedUpdatedReceiveShadows));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedRenderLayer", observedUpdatedRenderLayer));
|
|
|
|
EXPECT_TRUE(hasMeshFilter);
|
|
EXPECT_TRUE(hasMeshRenderer);
|
|
EXPECT_TRUE(meshFilterLookupSucceeded);
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_EQ(observedInitialMeshPath, "Meshes/initial.mesh");
|
|
EXPECT_EQ(observedUpdatedMeshPath, "Meshes/runtime_override.mesh");
|
|
EXPECT_EQ(observedInitialMaterialCount, 1);
|
|
EXPECT_EQ(observedInitialMaterial0Path, "Materials/initial.mat");
|
|
EXPECT_TRUE(observedInitialCastShadows);
|
|
EXPECT_FALSE(observedInitialReceiveShadows);
|
|
EXPECT_EQ(observedInitialRenderLayer, 7);
|
|
EXPECT_EQ(observedUpdatedMaterialCount, 2);
|
|
EXPECT_EQ(observedUpdatedMaterial1Path, "Materials/runtime_override.mat");
|
|
EXPECT_FALSE(observedUpdatedCastShadows);
|
|
EXPECT_TRUE(observedUpdatedReceiveShadows);
|
|
EXPECT_EQ(observedUpdatedRenderLayer, 11);
|
|
|
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/runtime_override.mesh");
|
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 2u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/initial.mat");
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(1), "Materials/runtime_override.mat");
|
|
EXPECT_FALSE(meshRenderer->GetCastShadows());
|
|
EXPECT_TRUE(meshRenderer->GetReceiveShadows());
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 11u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedMeshRendererWrapperHandlesClearAndBoundaryCases) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
MeshRendererComponent* meshRenderer = host->AddComponent<MeshRendererComponent>();
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "MeshRendererEdgeCaseProbe");
|
|
|
|
meshRenderer->SetMaterialPath(0, "Materials/initial0.mat");
|
|
meshRenderer->SetMaterialPath(1, "Materials/initial1.mat");
|
|
meshRenderer->SetCastShadows(false);
|
|
meshRenderer->SetReceiveShadows(true);
|
|
meshRenderer->SetRenderLayer(9);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedInitialMaterialCount = 0;
|
|
std::string observedNegativeIndexPath;
|
|
std::string observedOutOfRangePathBeforeClear;
|
|
std::string observedMaterial0PathAfterNegativeWrite;
|
|
std::string observedMaterial1PathAfterNegativeWrite;
|
|
int32_t observedMaterialCountAfterNegativeWrite = 0;
|
|
int32_t observedRenderLayerAfterNegativeWrite = -1;
|
|
int32_t observedMaterialCountAfterClear = -1;
|
|
std::string observedMaterial0PathAfterClear;
|
|
std::string observedMaterial3PathAfterClear;
|
|
bool observedCastShadowsAfterClear = true;
|
|
bool observedReceiveShadowsAfterClear = false;
|
|
int32_t observedRenderLayerAfterClear = -1;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialMaterialCount", observedInitialMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedNegativeIndexPath", observedNegativeIndexPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedOutOfRangePathBeforeClear", observedOutOfRangePathBeforeClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0PathAfterNegativeWrite", observedMaterial0PathAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial1PathAfterNegativeWrite", observedMaterial1PathAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCountAfterNegativeWrite", observedMaterialCountAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayerAfterNegativeWrite", observedRenderLayerAfterNegativeWrite));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCountAfterClear", observedMaterialCountAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0PathAfterClear", observedMaterial0PathAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial3PathAfterClear", observedMaterial3PathAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCastShadowsAfterClear", observedCastShadowsAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedReceiveShadowsAfterClear", observedReceiveShadowsAfterClear));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayerAfterClear", observedRenderLayerAfterClear));
|
|
|
|
EXPECT_EQ(observedInitialMaterialCount, 2);
|
|
EXPECT_EQ(observedNegativeIndexPath, "");
|
|
EXPECT_EQ(observedOutOfRangePathBeforeClear, "");
|
|
EXPECT_EQ(observedMaterial0PathAfterNegativeWrite, "Materials/initial0.mat");
|
|
EXPECT_EQ(observedMaterial1PathAfterNegativeWrite, "Materials/initial1.mat");
|
|
EXPECT_EQ(observedMaterialCountAfterNegativeWrite, 2);
|
|
EXPECT_EQ(observedRenderLayerAfterNegativeWrite, 0);
|
|
EXPECT_EQ(observedMaterialCountAfterClear, 0);
|
|
EXPECT_EQ(observedMaterial0PathAfterClear, "");
|
|
EXPECT_EQ(observedMaterial3PathAfterClear, "");
|
|
EXPECT_FALSE(observedCastShadowsAfterClear);
|
|
EXPECT_TRUE(observedReceiveShadowsAfterClear);
|
|
EXPECT_EQ(observedRenderLayerAfterClear, 0);
|
|
|
|
EXPECT_EQ(meshRenderer->GetMaterialCount(), 0u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "");
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(3), "");
|
|
EXPECT_FALSE(meshRenderer->GetCastShadows());
|
|
EXPECT_TRUE(meshRenderer->GetReceiveShadows());
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 0u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectAddComponentApiCreatesBuiltinComponentsAndAvoidsDuplicates) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "AddComponentProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool initialHasCamera = true;
|
|
bool initialHasLight = true;
|
|
bool initialHasMeshFilter = true;
|
|
bool initialHasMeshRenderer = true;
|
|
bool addedTransform = false;
|
|
bool addedCamera = false;
|
|
bool addedLight = false;
|
|
bool addedMeshFilter = false;
|
|
bool addedMeshRenderer = false;
|
|
bool hasCameraAfterAdd = false;
|
|
bool hasLightAfterAdd = false;
|
|
bool hasMeshFilterAfterAdd = false;
|
|
bool hasMeshRendererAfterAdd = false;
|
|
bool cameraLookupSucceeded = false;
|
|
bool lightLookupSucceeded = false;
|
|
bool meshFilterLookupSucceeded = false;
|
|
bool meshRendererLookupSucceeded = false;
|
|
float observedCameraFieldOfView = 0.0f;
|
|
float observedLightIntensity = 0.0f;
|
|
std::string observedMeshPath;
|
|
int32_t observedMaterialCount = 0;
|
|
std::string observedMaterial0Path;
|
|
int32_t observedRenderLayer = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasCamera", initialHasCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasLight", initialHasLight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasMeshFilter", initialHasMeshFilter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasMeshRenderer", initialHasMeshRenderer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedTransform", addedTransform));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedCamera", addedCamera));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedLight", addedLight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedMeshFilter", addedMeshFilter));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedMeshRenderer", addedMeshRenderer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasCameraAfterAdd", hasCameraAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasLightAfterAdd", hasLightAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshFilterAfterAdd", hasMeshFilterAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasMeshRendererAfterAdd", hasMeshRendererAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LightLookupSucceeded", lightLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshFilterLookupSucceeded", meshFilterLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCameraFieldOfView", observedCameraFieldOfView));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLightIntensity", observedLightIntensity));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMeshPath", observedMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCount", observedMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0Path", observedMaterial0Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayer", observedRenderLayer));
|
|
|
|
EXPECT_FALSE(initialHasCamera);
|
|
EXPECT_FALSE(initialHasLight);
|
|
EXPECT_FALSE(initialHasMeshFilter);
|
|
EXPECT_FALSE(initialHasMeshRenderer);
|
|
EXPECT_TRUE(addedTransform);
|
|
EXPECT_TRUE(addedCamera);
|
|
EXPECT_TRUE(addedLight);
|
|
EXPECT_TRUE(addedMeshFilter);
|
|
EXPECT_TRUE(addedMeshRenderer);
|
|
EXPECT_TRUE(hasCameraAfterAdd);
|
|
EXPECT_TRUE(hasLightAfterAdd);
|
|
EXPECT_TRUE(hasMeshFilterAfterAdd);
|
|
EXPECT_TRUE(hasMeshRendererAfterAdd);
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_TRUE(lightLookupSucceeded);
|
|
EXPECT_TRUE(meshFilterLookupSucceeded);
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_FLOAT_EQ(observedCameraFieldOfView, 82.0f);
|
|
EXPECT_FLOAT_EQ(observedLightIntensity, 4.5f);
|
|
EXPECT_EQ(observedMeshPath, "Meshes/added.mesh");
|
|
EXPECT_EQ(observedMaterialCount, 1);
|
|
EXPECT_EQ(observedMaterial0Path, "Materials/added.mat");
|
|
EXPECT_EQ(observedRenderLayer, 6);
|
|
|
|
ASSERT_NE(host->GetTransform(), nullptr);
|
|
EXPECT_EQ(host->GetComponents<CameraComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<LightComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<MeshFilterComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<MeshRendererComponent>().size(), 1u);
|
|
|
|
CameraComponent* camera = host->GetComponent<CameraComponent>();
|
|
LightComponent* light = host->GetComponent<LightComponent>();
|
|
MeshFilterComponent* meshFilter = host->GetComponent<MeshFilterComponent>();
|
|
MeshRendererComponent* meshRenderer = host->GetComponent<MeshRendererComponent>();
|
|
|
|
ASSERT_NE(camera, nullptr);
|
|
ASSERT_NE(light, nullptr);
|
|
ASSERT_NE(meshFilter, nullptr);
|
|
ASSERT_NE(meshRenderer, nullptr);
|
|
|
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 82.0f);
|
|
EXPECT_FLOAT_EQ(light->GetIntensity(), 4.5f);
|
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/added.mesh");
|
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 1u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/added.mat");
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 6u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectComponentApiSupportsManagedScriptTypes) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "ScriptComponentApiProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool initialHasTarget = true;
|
|
bool initialLookupReturnedNull = false;
|
|
bool addedTarget = false;
|
|
bool hasTargetAfterAdd = false;
|
|
bool lookupSucceededAfterAdd = false;
|
|
bool tryGetSucceededAfterAdd = false;
|
|
bool returnedSameInstance = false;
|
|
bool targetEnabledAfterAdd = false;
|
|
int32_t observedTargetAwakeCount = 0;
|
|
int32_t observedTargetStartCount = -1;
|
|
int32_t observedTargetHostCallCount = 0;
|
|
std::string observedTargetHostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialHasTarget", initialHasTarget));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "InitialLookupReturnedNull", initialLookupReturnedNull));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AddedTarget", addedTarget));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "HasTargetAfterAdd", hasTargetAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LookupSucceededAfterAdd", lookupSucceededAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TryGetSucceededAfterAdd", tryGetSucceededAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ReturnedSameInstance", returnedSameInstance));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "TargetEnabledAfterAdd", targetEnabledAfterAdd));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetAwakeCount", observedTargetAwakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetStartCount", observedTargetStartCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetHostCallCount", observedTargetHostCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedTargetHostName", observedTargetHostName));
|
|
|
|
EXPECT_FALSE(initialHasTarget);
|
|
EXPECT_TRUE(initialLookupReturnedNull);
|
|
EXPECT_TRUE(addedTarget);
|
|
EXPECT_TRUE(hasTargetAfterAdd);
|
|
EXPECT_TRUE(lookupSucceededAfterAdd);
|
|
EXPECT_TRUE(tryGetSucceededAfterAdd);
|
|
EXPECT_TRUE(returnedSameInstance);
|
|
EXPECT_TRUE(targetEnabledAfterAdd);
|
|
EXPECT_EQ(observedTargetAwakeCount, 1);
|
|
EXPECT_EQ(observedTargetStartCount, 0);
|
|
EXPECT_EQ(observedTargetHostCallCount, 1);
|
|
EXPECT_EQ(observedTargetHostName, "Host");
|
|
|
|
ASSERT_EQ(host->GetComponents<ScriptComponent>().size(), 2u);
|
|
ScriptComponent* targetScript = FindScriptComponentByClass(host, "Gameplay", "ScriptComponentTargetProbe");
|
|
ASSERT_NE(targetScript, nullptr);
|
|
EXPECT_TRUE(runtime->HasManagedInstance(targetScript));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 2u);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t startCount = -1;
|
|
int32_t hostCallCount = 0;
|
|
std::string hostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "HostCallCount", hostCallCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "HostName", hostName));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(startCount, 0);
|
|
EXPECT_EQ(hostCallCount, 1);
|
|
EXPECT_EQ(hostName, "Host");
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
startCount = 0;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(targetScript, "StartCount", startCount));
|
|
EXPECT_EQ(startCount, 1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectRuntimeApiCreatesFindsAndDestroysSceneObjects) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "RuntimeGameObjectProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool missingBeforeCreate = false;
|
|
bool createdRootSucceeded = false;
|
|
bool createdChildSucceeded = false;
|
|
bool foundRootSucceeded = false;
|
|
bool foundChildSucceeded = false;
|
|
std::string observedFoundRootName;
|
|
std::string observedFoundChildParentName;
|
|
int32_t observedRootChildCountBeforeDestroy = 0;
|
|
bool cameraLookupSucceeded = false;
|
|
bool meshFilterLookupSucceeded = false;
|
|
bool meshRendererLookupSucceeded = false;
|
|
float observedCameraFieldOfView = 0.0f;
|
|
std::string observedMeshPath;
|
|
int32_t observedMaterialCount = 0;
|
|
std::string observedMaterial0Path;
|
|
int32_t observedRenderLayer = 0;
|
|
bool missingChildAfterDestroy = false;
|
|
bool foundRootAfterDestroySucceeded = false;
|
|
int32_t observedRootChildCountAfterDestroy = -1;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MissingBeforeCreate", missingBeforeCreate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CreatedRootSucceeded", createdRootSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CreatedChildSucceeded", createdChildSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundRootSucceeded", foundRootSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundChildSucceeded", foundChildSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFoundRootName", observedFoundRootName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFoundChildParentName", observedFoundChildParentName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountBeforeDestroy", observedRootChildCountBeforeDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "CameraLookupSucceeded", cameraLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshFilterLookupSucceeded", meshFilterLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MeshRendererLookupSucceeded", meshRendererLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedCameraFieldOfView", observedCameraFieldOfView));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMeshPath", observedMeshPath));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterialCount", observedMaterialCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedMaterial0Path", observedMaterial0Path));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRenderLayer", observedRenderLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "MissingChildAfterDestroy", missingChildAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "FoundRootAfterDestroySucceeded", foundRootAfterDestroySucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRootChildCountAfterDestroy", observedRootChildCountAfterDestroy));
|
|
|
|
EXPECT_TRUE(missingBeforeCreate);
|
|
EXPECT_TRUE(createdRootSucceeded);
|
|
EXPECT_TRUE(createdChildSucceeded);
|
|
EXPECT_TRUE(foundRootSucceeded);
|
|
EXPECT_TRUE(foundChildSucceeded);
|
|
EXPECT_EQ(observedFoundRootName, "RuntimeCreatedRoot");
|
|
EXPECT_EQ(observedFoundChildParentName, "RuntimeCreatedRoot");
|
|
EXPECT_EQ(observedRootChildCountBeforeDestroy, 1);
|
|
EXPECT_TRUE(cameraLookupSucceeded);
|
|
EXPECT_TRUE(meshFilterLookupSucceeded);
|
|
EXPECT_TRUE(meshRendererLookupSucceeded);
|
|
EXPECT_FLOAT_EQ(observedCameraFieldOfView, 68.0f);
|
|
EXPECT_EQ(observedMeshPath, "Meshes/runtime_created.mesh");
|
|
EXPECT_EQ(observedMaterialCount, 1);
|
|
EXPECT_EQ(observedMaterial0Path, "Materials/runtime_created.mat");
|
|
EXPECT_EQ(observedRenderLayer, 4);
|
|
EXPECT_TRUE(missingChildAfterDestroy);
|
|
EXPECT_TRUE(foundRootAfterDestroySucceeded);
|
|
EXPECT_EQ(observedRootChildCountAfterDestroy, 0);
|
|
|
|
GameObject* createdRoot = runtimeScene->Find("RuntimeCreatedRoot");
|
|
ASSERT_NE(createdRoot, nullptr);
|
|
EXPECT_EQ(runtimeScene->Find("RuntimeCreatedChild"), nullptr);
|
|
EXPECT_EQ(createdRoot->GetParent(), nullptr);
|
|
EXPECT_EQ(createdRoot->GetChildCount(), 0u);
|
|
|
|
CameraComponent* camera = createdRoot->GetComponent<CameraComponent>();
|
|
MeshFilterComponent* meshFilter = createdRoot->GetComponent<MeshFilterComponent>();
|
|
MeshRendererComponent* meshRenderer = createdRoot->GetComponent<MeshRendererComponent>();
|
|
|
|
ASSERT_NE(camera, nullptr);
|
|
ASSERT_NE(meshFilter, nullptr);
|
|
ASSERT_NE(meshRenderer, nullptr);
|
|
EXPECT_FLOAT_EQ(camera->GetFieldOfView(), 68.0f);
|
|
EXPECT_EQ(meshFilter->GetMeshPath(), "Meshes/runtime_created.mesh");
|
|
ASSERT_EQ(meshRenderer->GetMaterialCount(), 1u);
|
|
EXPECT_EQ(meshRenderer->GetMaterialPath(0), "Materials/runtime_created.mat");
|
|
EXPECT_EQ(meshRenderer->GetRenderLayer(), 4u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, GameObjectTagAndLayerApiExposeUnityStylePropertiesAndCompareTag) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
host->SetTag("Enemy");
|
|
host->SetLayer(7);
|
|
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TagLayerProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
std::string observedInitialTag;
|
|
int32_t observedInitialLayer = -1;
|
|
bool observedInitialCompareTag = false;
|
|
bool observedGameObjectRouteMatches = false;
|
|
std::string observedUpdatedTag;
|
|
int32_t observedUpdatedLayer = -1;
|
|
bool observedUpdatedCompareTag = false;
|
|
bool observedOriginalTagRejected = false;
|
|
bool observedEmptyTagRejected = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialTag", observedInitialTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialLayer", observedInitialLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialCompareTag", observedInitialCompareTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectRouteMatches", observedGameObjectRouteMatches));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedTag", observedUpdatedTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedLayer", observedUpdatedLayer));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpdatedCompareTag", observedUpdatedCompareTag));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedOriginalTagRejected", observedOriginalTagRejected));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEmptyTagRejected", observedEmptyTagRejected));
|
|
|
|
EXPECT_EQ(observedInitialTag, "Enemy");
|
|
EXPECT_EQ(observedInitialLayer, 7);
|
|
EXPECT_TRUE(observedInitialCompareTag);
|
|
EXPECT_TRUE(observedGameObjectRouteMatches);
|
|
EXPECT_EQ(observedUpdatedTag, "Player");
|
|
EXPECT_EQ(observedUpdatedLayer, 31);
|
|
EXPECT_TRUE(observedUpdatedCompareTag);
|
|
EXPECT_TRUE(observedOriginalTagRejected);
|
|
EXPECT_TRUE(observedEmptyTagRejected);
|
|
|
|
EXPECT_EQ(host->GetTag(), "Player");
|
|
EXPECT_EQ(host->GetLayer(), 31u);
|
|
EXPECT_EQ(runtimeScene->FindGameObjectWithTag("Player"), host);
|
|
EXPECT_EQ(runtimeScene->FindGameObjectWithTag("Enemy"), nullptr);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, RuntimeCreatedScriptComponentCreatesManagedInstanceAfterClassAssignment) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
|
|
GameObject* spawned = runtimeScene->CreateGameObject("RuntimeSpawned");
|
|
ScriptComponent* component = spawned->AddComponent<ScriptComponent>();
|
|
component->GetFieldStorage().SetFieldValue("Label", "RuntimeConfigured");
|
|
component->SetScriptClass("GameScripts", "Gameplay", "LifecycleProbe");
|
|
|
|
EXPECT_TRUE(runtime->HasManagedInstance(component));
|
|
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t awakeCount = 0;
|
|
int32_t enableCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t updateCount = 0;
|
|
std::string label;
|
|
std::string observedGameObjectName;
|
|
bool wasAwakened = false;
|
|
bool observedEnabled = false;
|
|
bool observedActiveSelf = false;
|
|
bool observedActiveInHierarchy = false;
|
|
bool observedIsActiveAndEnabled = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "AwakeCount", awakeCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "Label", label));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedGameObjectName", observedGameObjectName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "WasAwakened", wasAwakened));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
|
|
|
EXPECT_EQ(awakeCount, 1);
|
|
EXPECT_EQ(enableCount, 1);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(label, "RuntimeConfigured|Awake");
|
|
EXPECT_EQ(observedGameObjectName, "RuntimeSpawned_Managed");
|
|
EXPECT_TRUE(wasAwakened);
|
|
EXPECT_TRUE(observedEnabled);
|
|
EXPECT_TRUE(observedActiveSelf);
|
|
EXPECT_TRUE(observedActiveInHierarchy);
|
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
|
EXPECT_EQ(spawned->GetName(), "RuntimeSpawned_Managed");
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformHierarchyApiExposesParentChildAndReparenting) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* root = runtimeScene->CreateGameObject("Root");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host", root);
|
|
GameObject* child = runtimeScene->CreateGameObject("Child", host);
|
|
GameObject* reparentTarget = runtimeScene->CreateGameObject("ReparentTarget");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "HierarchyProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("ReparentTarget", GameObjectReference{reparentTarget->GetUUID()});
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedChildCount = 0;
|
|
bool parentLookupSucceeded = false;
|
|
bool childLookupSucceeded = false;
|
|
std::string observedParentName;
|
|
std::string observedFirstChildName;
|
|
std::string reparentedChildParentName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedChildCount", observedChildCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ParentLookupSucceeded", parentLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ChildLookupSucceeded", childLookupSucceeded));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedParentName", observedParentName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedFirstChildName", observedFirstChildName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ReparentedChildParentName", reparentedChildParentName));
|
|
|
|
EXPECT_TRUE(parentLookupSucceeded);
|
|
EXPECT_TRUE(childLookupSucceeded);
|
|
EXPECT_EQ(observedChildCount, 1);
|
|
EXPECT_EQ(observedParentName, "Root");
|
|
EXPECT_EQ(observedFirstChildName, "Child");
|
|
EXPECT_EQ(reparentedChildParentName, "ReparentTarget");
|
|
|
|
EXPECT_EQ(host->GetParent(), root);
|
|
EXPECT_EQ(host->GetChildCount(), 0u);
|
|
EXPECT_EQ(child->GetParent(), reparentTarget);
|
|
EXPECT_EQ(reparentTarget->GetChildCount(), 1u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, UnityObjectApiGetComponentsReturnsDirectComponentsAndReusesManagedInstances) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
GameObject* child = runtimeScene->CreateGameObject("Child", host);
|
|
|
|
host->AddComponent<CameraComponent>();
|
|
child->AddComponent<CameraComponent>();
|
|
child->AddComponent<MeshRendererComponent>();
|
|
|
|
AddScript(host, "Gameplay", "ObjectApiMarkerProbe");
|
|
AddScript(host, "Gameplay", "ObjectApiMarkerProbe");
|
|
ScriptComponent* hostProbe = AddScript(host, "Gameplay", "GetComponentsProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t observedTransformCount = -1;
|
|
int32_t observedCameraCount = -1;
|
|
int32_t observedCameraCountViaGameObject = -1;
|
|
int32_t observedMarkerCount = -1;
|
|
int32_t observedMeshRendererCount = -1;
|
|
bool observedAllMarkersNonNull = false;
|
|
bool observedFirstMarkerMatchesGetComponent = false;
|
|
bool observedMarkerInstancesAreDistinct = false;
|
|
bool observedTransformBoundToHost = false;
|
|
std::string observedFirstMarkerHostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedTransformCount", observedTransformCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedCameraCount", observedCameraCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedCameraCountViaGameObject", observedCameraCountViaGameObject));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedMarkerCount", observedMarkerCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedMeshRendererCount", observedMeshRendererCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedAllMarkersNonNull", observedAllMarkersNonNull));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedFirstMarkerMatchesGetComponent", observedFirstMarkerMatchesGetComponent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedMarkerInstancesAreDistinct", observedMarkerInstancesAreDistinct));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedTransformBoundToHost", observedTransformBoundToHost));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedFirstMarkerHostName", observedFirstMarkerHostName));
|
|
|
|
EXPECT_EQ(observedTransformCount, 1);
|
|
EXPECT_EQ(observedCameraCount, 1);
|
|
EXPECT_EQ(observedCameraCountViaGameObject, 1);
|
|
EXPECT_EQ(observedMarkerCount, 2);
|
|
EXPECT_EQ(observedMeshRendererCount, 0);
|
|
EXPECT_TRUE(observedAllMarkersNonNull);
|
|
EXPECT_TRUE(observedFirstMarkerMatchesGetComponent);
|
|
EXPECT_TRUE(observedMarkerInstancesAreDistinct);
|
|
EXPECT_TRUE(observedTransformBoundToHost);
|
|
EXPECT_EQ(observedFirstMarkerHostName, "Host");
|
|
|
|
EXPECT_EQ(host->GetComponents<CameraComponent>().size(), 1u);
|
|
EXPECT_EQ(host->GetComponents<ScriptComponent>().size(), 3u);
|
|
EXPECT_EQ(child->GetComponents<CameraComponent>().size(), 1u);
|
|
EXPECT_EQ(child->GetComponents<MeshRendererComponent>().size(), 1u);
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 3u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, UnityObjectApiSupportsHierarchyLookupAndDestroy) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* root = runtimeScene->CreateGameObject("Root");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host", root);
|
|
GameObject* child = runtimeScene->CreateGameObject("Child", host);
|
|
|
|
root->AddComponent<CameraComponent>();
|
|
child->AddComponent<CameraComponent>();
|
|
child->AddComponent<MeshRendererComponent>();
|
|
|
|
AddScript(root, "Gameplay", "ObjectApiMarkerProbe");
|
|
AddScript(child, "Gameplay", "ObjectApiDestroyTargetProbe");
|
|
ScriptComponent* hostProbe = AddScript(host, "Gameplay", "ObjectApiProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
bool foundSelfScriptViaParentLookup = false;
|
|
bool foundSelfScriptViaGameObjectParentLookup = false;
|
|
bool foundSelfTransformViaChildrenLookup = false;
|
|
bool foundSelfTransformViaGameObjectChildrenLookup = false;
|
|
bool foundCameraInParent = false;
|
|
bool foundCameraInParentViaGameObject = false;
|
|
bool foundMarkerInParent = false;
|
|
bool foundMeshRendererInChildren = false;
|
|
bool foundMeshRendererInChildrenViaGameObject = false;
|
|
bool foundTargetScriptInChildren = false;
|
|
bool childCameraMissingAfterDestroy = false;
|
|
bool childScriptMissingAfterDestroy = false;
|
|
bool childGameObjectMissingAfterDestroy = false;
|
|
int32_t observedChildScriptDisableCount = -1;
|
|
int32_t observedChildCountAfterDestroy = -1;
|
|
std::string observedParentCameraHostName;
|
|
std::string observedChildMeshRendererHostName;
|
|
std::string observedChildScriptHostName;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfScriptViaParentLookup", foundSelfScriptViaParentLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfScriptViaGameObjectParentLookup", foundSelfScriptViaGameObjectParentLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfTransformViaChildrenLookup", foundSelfTransformViaChildrenLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundSelfTransformViaGameObjectChildrenLookup", foundSelfTransformViaGameObjectChildrenLookup));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundCameraInParent", foundCameraInParent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundCameraInParentViaGameObject", foundCameraInParentViaGameObject));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundMarkerInParent", foundMarkerInParent));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundMeshRendererInChildren", foundMeshRendererInChildren));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundMeshRendererInChildrenViaGameObject", foundMeshRendererInChildrenViaGameObject));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "FoundTargetScriptInChildren", foundTargetScriptInChildren));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ChildCameraMissingAfterDestroy", childCameraMissingAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ChildScriptMissingAfterDestroy", childScriptMissingAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ChildGameObjectMissingAfterDestroy", childGameObjectMissingAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildScriptDisableCount", observedChildScriptDisableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildCountAfterDestroy", observedChildCountAfterDestroy));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedParentCameraHostName", observedParentCameraHostName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildMeshRendererHostName", observedChildMeshRendererHostName));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(hostProbe, "ObservedChildScriptHostName", observedChildScriptHostName));
|
|
|
|
EXPECT_TRUE(foundSelfScriptViaParentLookup);
|
|
EXPECT_TRUE(foundSelfScriptViaGameObjectParentLookup);
|
|
EXPECT_TRUE(foundSelfTransformViaChildrenLookup);
|
|
EXPECT_TRUE(foundSelfTransformViaGameObjectChildrenLookup);
|
|
EXPECT_TRUE(foundCameraInParent);
|
|
EXPECT_TRUE(foundCameraInParentViaGameObject);
|
|
EXPECT_TRUE(foundMarkerInParent);
|
|
EXPECT_TRUE(foundMeshRendererInChildren);
|
|
EXPECT_TRUE(foundMeshRendererInChildrenViaGameObject);
|
|
EXPECT_TRUE(foundTargetScriptInChildren);
|
|
EXPECT_TRUE(childCameraMissingAfterDestroy);
|
|
EXPECT_TRUE(childScriptMissingAfterDestroy);
|
|
EXPECT_TRUE(childGameObjectMissingAfterDestroy);
|
|
EXPECT_EQ(observedChildScriptDisableCount, 1);
|
|
EXPECT_EQ(observedChildCountAfterDestroy, 0);
|
|
EXPECT_EQ(observedParentCameraHostName, "Root");
|
|
EXPECT_EQ(observedChildMeshRendererHostName, "Child");
|
|
EXPECT_EQ(observedChildScriptHostName, "Child");
|
|
|
|
EXPECT_EQ(runtimeScene->Find("Child"), nullptr);
|
|
EXPECT_EQ(host->GetChildCount(), 0u);
|
|
EXPECT_EQ(host->GetComponentInChildren<CameraComponent>(), nullptr);
|
|
EXPECT_EQ(host->GetComponentInChildren<MeshRendererComponent>(), nullptr);
|
|
EXPECT_EQ(host->GetComponents<ScriptComponent>().size(), 1u);
|
|
EXPECT_EQ(root->GetComponents<ScriptComponent>().size(), 1u);
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 2u);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformSpaceApiReadsAndWritesWorldAndLocalValues) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* parent = runtimeScene->CreateGameObject("Parent");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host", parent);
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformSpaceProbe");
|
|
|
|
parent->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(10.0f, 0.0f, 0.0f));
|
|
parent->GetTransform()->SetLocalScale(XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
|
host->GetTransform()->SetLocalPosition(XCEngine::Math::Vector3(1.0f, 2.0f, 3.0f));
|
|
host->GetTransform()->SetLocalScale(XCEngine::Math::Vector3(1.0f, 1.0f, 1.0f));
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedInitialWorldPosition;
|
|
XCEngine::Math::Vector3 observedInitialWorldScale;
|
|
XCEngine::Math::Vector3 observedInitialLocalEulerAngles;
|
|
XCEngine::Math::Vector3 observedEulerAfterSet;
|
|
XCEngine::Math::Vector3 observedWorldPositionAfterSet;
|
|
XCEngine::Math::Vector3 observedWorldScaleAfterSet;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialWorldPosition", observedInitialWorldPosition));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialWorldScale", observedInitialWorldScale));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedInitialLocalEulerAngles", observedInitialLocalEulerAngles));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEulerAfterSet", observedEulerAfterSet));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldPositionAfterSet", observedWorldPositionAfterSet));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldScaleAfterSet", observedWorldScaleAfterSet));
|
|
|
|
EXPECT_EQ(observedInitialWorldPosition, XCEngine::Math::Vector3(12.0f, 4.0f, 6.0f));
|
|
EXPECT_EQ(observedInitialWorldScale, XCEngine::Math::Vector3(2.0f, 2.0f, 2.0f));
|
|
EXPECT_EQ(observedInitialLocalEulerAngles, XCEngine::Math::Vector3(0.0f, 0.0f, 0.0f));
|
|
EXPECT_NEAR(observedEulerAfterSet.x, 0.0f, 0.001f);
|
|
EXPECT_NEAR(observedEulerAfterSet.y, 45.0f, 0.001f);
|
|
EXPECT_NEAR(observedEulerAfterSet.z, 0.0f, 0.001f);
|
|
EXPECT_EQ(observedWorldPositionAfterSet, XCEngine::Math::Vector3(20.0f, 6.0f, 10.0f));
|
|
EXPECT_EQ(observedWorldScaleAfterSet, XCEngine::Math::Vector3(6.0f, 8.0f, 10.0f));
|
|
|
|
EXPECT_EQ(host->GetTransform()->GetLocalPosition(), XCEngine::Math::Vector3(5.0f, 3.0f, 5.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetPosition(), XCEngine::Math::Vector3(20.0f, 6.0f, 10.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetLocalScale(), XCEngine::Math::Vector3(3.0f, 4.0f, 5.0f));
|
|
EXPECT_EQ(host->GetTransform()->GetScale(), XCEngine::Math::Vector3(6.0f, 8.0f, 10.0f));
|
|
|
|
const XCEngine::Math::Quaternion& localRotation = host->GetTransform()->GetLocalRotation();
|
|
EXPECT_FLOAT_EQ(localRotation.x, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.y, 0.5f);
|
|
EXPECT_FLOAT_EQ(localRotation.z, 0.0f);
|
|
EXPECT_FLOAT_EQ(localRotation.w, 0.8660254f);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformMotionApiExposesDirectionVectorsAndAppliesOperations) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformMotionProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedForward;
|
|
XCEngine::Math::Vector3 observedRight;
|
|
XCEngine::Math::Vector3 observedUp;
|
|
XCEngine::Math::Vector3 observedPositionAfterSelfTranslate;
|
|
XCEngine::Math::Vector3 observedPositionAfterWorldTranslate;
|
|
XCEngine::Math::Vector3 observedForwardAfterWorldRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterSelfRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterLookAt;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForward", observedForward));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRight", observedRight));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUp", observedUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPositionAfterSelfTranslate", observedPositionAfterSelfTranslate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedPositionAfterWorldTranslate", observedPositionAfterWorldTranslate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterWorldRotate", observedForwardAfterWorldRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterSelfRotate", observedForwardAfterSelfRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterLookAt", observedForwardAfterLookAt));
|
|
|
|
ExpectVector3Near(observedForward, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedRight, XCEngine::Math::Vector3(0.0f, 0.0f, -1.0f));
|
|
ExpectVector3Near(observedUp, XCEngine::Math::Vector3(0.0f, 1.0f, 0.0f));
|
|
ExpectVector3Near(observedPositionAfterSelfTranslate, XCEngine::Math::Vector3(2.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedPositionAfterWorldTranslate, XCEngine::Math::Vector3(2.0f, 0.0f, 3.0f));
|
|
ExpectVector3Near(observedForwardAfterWorldRotate, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
ExpectVector3Near(observedForwardAfterSelfRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedForwardAfterLookAt, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
|
|
ExpectVector3Near(host->GetTransform()->GetPosition(), XCEngine::Math::Vector3(2.0f, 0.0f, 3.0f));
|
|
ExpectVector3Near(host->GetTransform()->GetForward(), XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformConversionApiTransformsPointsAndDirectionsBetweenSpaces) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformConversionProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedWorldPoint;
|
|
XCEngine::Math::Vector3 observedLocalPoint;
|
|
XCEngine::Math::Vector3 observedWorldDirection;
|
|
XCEngine::Math::Vector3 observedLocalDirection;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldPoint", observedWorldPoint));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalPoint", observedLocalPoint));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedWorldDirection", observedWorldDirection));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedLocalDirection", observedLocalDirection));
|
|
|
|
ExpectVector3Near(observedWorldPoint, XCEngine::Math::Vector3(7.0f, 0.0f, 1.0f));
|
|
ExpectVector3Near(observedLocalPoint, XCEngine::Math::Vector3(0.0f, 0.0f, 2.0f));
|
|
ExpectVector3Near(observedWorldDirection, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedLocalDirection, XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
|
|
ExpectVector3Near(host->GetTransform()->TransformPoint(XCEngine::Math::Vector3(0.0f, 0.0f, 2.0f)), XCEngine::Math::Vector3(7.0f, 0.0f, 1.0f));
|
|
ExpectVector3Near(host->GetTransform()->InverseTransformDirection(XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f)), XCEngine::Math::Vector3(0.0f, 0.0f, 1.0f));
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, TransformOrientationOverloadsSupportAxisAngleAndCustomUpVector) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "TransformOrientationProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
XCEngine::Math::Vector3 observedForwardAfterAxisAngleRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterWorldAxisAngleRotate;
|
|
XCEngine::Math::Vector3 observedForwardAfterDefaultLookAt;
|
|
XCEngine::Math::Vector3 observedRightAfterDefaultLookAt;
|
|
XCEngine::Math::Vector3 observedUpAfterDefaultLookAt;
|
|
XCEngine::Math::Vector3 observedForwardAfterLookAtWithUp;
|
|
XCEngine::Math::Vector3 observedRightAfterLookAtWithUp;
|
|
XCEngine::Math::Vector3 observedUpAfterLookAtWithUp;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterAxisAngleRotate", observedForwardAfterAxisAngleRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterWorldAxisAngleRotate", observedForwardAfterWorldAxisAngleRotate));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterDefaultLookAt", observedForwardAfterDefaultLookAt));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRightAfterDefaultLookAt", observedRightAfterDefaultLookAt));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpAfterDefaultLookAt", observedUpAfterDefaultLookAt));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedForwardAfterLookAtWithUp", observedForwardAfterLookAtWithUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedRightAfterLookAtWithUp", observedRightAfterLookAtWithUp));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedUpAfterLookAtWithUp", observedUpAfterLookAtWithUp));
|
|
|
|
ExpectVector3Near(observedForwardAfterAxisAngleRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
ExpectVector3Near(observedForwardAfterWorldAxisAngleRotate, XCEngine::Math::Vector3(1.0f, 0.0f, 0.0f));
|
|
|
|
EXPECT_NEAR(observedForwardAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedRightAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedUpAfterDefaultLookAt.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedForwardAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedRightAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f);
|
|
EXPECT_NEAR(observedUpAfterLookAtWithUp.Magnitude(), 1.0f, 0.001f);
|
|
|
|
EXPECT_GT((observedRightAfterLookAtWithUp - observedRightAfterDefaultLookAt).Magnitude(), 0.5f);
|
|
EXPECT_GT((observedUpAfterLookAtWithUp - observedUpAfterDefaultLookAt).Magnitude(), 0.5f);
|
|
|
|
ExpectVector3Near(host->GetTransform()->GetForward(), observedForwardAfterLookAtWithUp);
|
|
ExpectVector3Near(host->GetTransform()->GetRight(), observedRightAfterLookAtWithUp);
|
|
ExpectVector3Near(host->GetTransform()->GetUp(), observedUpAfterLookAtWithUp);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedBehaviourCanDisableItselfThroughEnabledProperty) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("DisableSelfOnFirstUpdate", true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t updateCount = 0;
|
|
int32_t lateUpdateCount = 0;
|
|
int32_t disableCount = 0;
|
|
bool observedEnabled = false;
|
|
bool observedIsActiveAndEnabled = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedEnabled", observedEnabled));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedIsActiveAndEnabled", observedIsActiveAndEnabled));
|
|
|
|
EXPECT_FALSE(component->IsEnabled());
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(lateUpdateCount, 0);
|
|
EXPECT_EQ(disableCount, 1);
|
|
EXPECT_TRUE(observedEnabled);
|
|
EXPECT_TRUE(observedIsActiveAndEnabled);
|
|
|
|
engine->OnUpdate(0.016f);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_EQ(updateCount, 1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, ManagedScriptCanDeactivateItsHostGameObject) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
component->GetFieldStorage().SetFieldValue("DeactivateGameObjectOnFirstUpdate", true);
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
engine->OnLateUpdate(0.016f);
|
|
|
|
int32_t updateCount = 0;
|
|
int32_t lateUpdateCount = 0;
|
|
int32_t disableCount = 0;
|
|
bool observedActiveSelf = false;
|
|
bool observedActiveInHierarchy = false;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "LateUpdateCount", lateUpdateCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveSelf", observedActiveSelf));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "ObservedActiveInHierarchy", observedActiveInHierarchy));
|
|
|
|
EXPECT_FALSE(host->IsActive());
|
|
EXPECT_FALSE(host->IsActiveInHierarchy());
|
|
EXPECT_TRUE(component->IsEnabled());
|
|
EXPECT_EQ(updateCount, 1);
|
|
EXPECT_EQ(lateUpdateCount, 0);
|
|
EXPECT_EQ(disableCount, 1);
|
|
EXPECT_TRUE(observedActiveSelf);
|
|
EXPECT_TRUE(observedActiveInHierarchy);
|
|
|
|
engine->OnUpdate(0.016f);
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
EXPECT_EQ(updateCount, 1);
|
|
}
|
|
|
|
TEST_F(MonoScriptRuntimeTest, DisableEnablePreservesSingleInstanceAndStartOnlyRunsOnce) {
|
|
Scene* runtimeScene = CreateScene("MonoRuntimeScene");
|
|
GameObject* host = runtimeScene->CreateGameObject("Host");
|
|
ScriptComponent* component = AddScript(host, "Gameplay", "LifecycleProbe");
|
|
|
|
engine->OnRuntimeStart(runtimeScene);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
component->SetEnabled(false);
|
|
|
|
int32_t disableCount = 0;
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "DisableCount", disableCount));
|
|
EXPECT_EQ(disableCount, 1);
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 1u);
|
|
|
|
component->SetEnabled(true);
|
|
engine->OnUpdate(0.016f);
|
|
|
|
int32_t enableCount = 0;
|
|
int32_t startCount = 0;
|
|
int32_t updateCount = 0;
|
|
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "EnableCount", enableCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "StartCount", startCount));
|
|
EXPECT_TRUE(runtime->TryGetFieldValue(component, "UpdateCount", updateCount));
|
|
|
|
EXPECT_EQ(enableCount, 2);
|
|
EXPECT_EQ(startCount, 1);
|
|
EXPECT_EQ(updateCount, 2);
|
|
|
|
engine->OnRuntimeStop();
|
|
|
|
EXPECT_FALSE(runtime->HasManagedInstance(component));
|
|
EXPECT_EQ(runtime->GetManagedInstanceCount(), 0u);
|
|
}
|
|
|
|
} // namespace
|