Refactor editor scene document ownership into runtime coordinator

This commit is contained in:
2026-04-29 16:24:06 +08:00
parent 749417989a
commit 631bf32db2
9 changed files with 197 additions and 78 deletions

View File

@@ -15,6 +15,8 @@ The primary product line is still runtime/product loop closure:
- keep play mode transactional: `EditorRuntimeCoordinator` must enter play
through `EditorSceneRuntime::BeginPlaySession`, and `RuntimeLoop` must run
the play-session runtime scene rather than the editable document scene
- drive `EditorRuntimeCoordinator` from the app frame pump exactly once per
outer frame; do not tick runtime from per-window shell/content update paths
- bind a real script assembly builder behind `scripts.*`; until then the
coordinator must keep the command honestly disabled
- keep `Game`, `Scene`, `Inspector`, `Selection`, and `Console` coherent across
@@ -62,14 +64,18 @@ Rules:
- `EditorHostCommandBridge` delegates `file.*`, `run.*`, and `scripts.*` to a
bound runtime owner. If no owner is bound, it must continue exposing honest
disabled messages.
- `EditorSceneRuntime` owns raw scene editing behavior and document-adjacent
scene mutations. Scene viewport private state now lives in
- `EditorSceneRuntime` owns raw scene editing behavior and scene mutations. It
may return transient startup/open/new results, but it must not own the live
scene document state. Scene viewport private state now lives in
`SceneViewportSession`, owned by the scene viewport feature/panel instance,
not by `EditorContext`, `EditorFrameServices`, or the shared scene runtime.
`EditorRuntimeCoordinator` owns scene document state: current path/name,
dirty flag, new/open/save routing, and runtime-mode transitions.
- `EditorRuntimeCoordinator` time advancement is app-owned. It must tick once
per outer application frame before window rendering, not once per workspace
window or once per shell update.
- `ProjectPanel` may identify an openable scene asset, but scene document loading
must go through `EditorFrameServices::RequestOpenSceneAsset` and the
must go through the bound typed scene-open request callback and the
coordinator. Do not reintroduce `ProjectRuntime` pending-open queues.
- `scripts.rebuild` has a bound coordinator owner, but no in-process script
assembly builder is currently wired. Keep it disabled and explicit until that
@@ -81,7 +87,8 @@ Rules:
stay on the window/content and shell-orchestration seam only. It must not be
passed through the workspace-panel seam or the utility-window panel seam as a
generic dependency source; workspace panels and utility panels should receive
explicit panel-local bindings or typed callbacks instead
explicit panel-local bindings or typed callbacks instead. Runtime time-step
ownership does not belong on this interface.
## Working Rules
@@ -118,6 +125,10 @@ Rules:
dependency bundle
- no `EditorFrameServices` tunneling into workspace-panel update/prepare paths
- no `EditorFrameServices` tunneling into utility-window panel update paths
- no per-window or per-shell runtime ticking path; runtime must not advance once
per workspace window
- no new live scene document metadata on `EditorSceneRuntime`; coordinator
remains the single owner for current path/name/dirty state
- no new path where Scene viewport render requests or camera/tool state are
recomputed from `EditorContext` instead of the owning `SceneViewportSession`
- no scene-open side channel in `EditorProjectRuntime`; project UI must call the

View File

@@ -74,7 +74,9 @@ bool EditorContext::Initialize(
m_projectRuntime.BindSelectionService(&m_selectionService);
m_sceneRuntime.SetBackend(sceneBackendFactory.CreateSceneBackend());
AppendUIEditorRuntimeTrace("startup", "EditorSceneRuntime::Initialize begin");
if (!m_sceneRuntime.Initialize(m_session.projectRoot)) {
const EditorStartupSceneResult startupScene =
m_sceneRuntime.Initialize(m_session.projectRoot);
if (!startupScene.ready) {
m_validationMessage = "Editor scene runtime failed to initialize.";
AppendUIEditorRuntimeTrace("startup", m_validationMessage);
return false;
@@ -85,7 +87,8 @@ bool EditorContext::Initialize(
m_session,
m_sceneRuntime,
m_projectRuntime,
runtimePaths);
runtimePaths,
startupScene);
ResetEditorColorPickerToolState(m_colorPickerToolState);
ResetEditorUtilityWindowRequestState(m_utilityWindowRequestState);
SyncSessionFromSelectionService();

View File

@@ -26,6 +26,10 @@ struct EditorStartupSceneResult {
bool loadedFromDisk = false;
std::filesystem::path scenePath = {};
std::string sceneName = {};
operator bool() const {
return ready;
}
};
using EditorSceneObjectId = std::uint64_t;

View File

@@ -50,6 +50,14 @@ std::string ResolveSceneDisplayName(const std::filesystem::path& scenePath) {
: scenePath.stem().string();
}
std::string ResolveDocumentSceneName(
std::string_view sceneName,
const std::filesystem::path& scenePath) {
return !sceneName.empty()
? std::string(sceneName)
: ResolveSceneDisplayName(scenePath);
}
} // namespace
EditorRuntimeCoordinator::~EditorRuntimeCoordinator() {
@@ -60,21 +68,15 @@ void EditorRuntimeCoordinator::Initialize(
EditorSession& session,
EditorSceneRuntime& sceneRuntime,
EditorProjectRuntime& projectRuntime,
const EditorRuntimePaths& runtimePaths) {
const EditorRuntimePaths& runtimePaths,
const EditorStartupSceneResult& startupScene) {
Shutdown();
m_session = &session;
m_sceneRuntime = &sceneRuntime;
m_projectRuntime = &projectRuntime;
m_runtimePaths = runtimePaths;
const EditorStartupSceneResult& startupScene =
m_sceneRuntime->GetStartupResult();
m_session->currentScenePath = startupScene.scenePath;
m_session->currentSceneName = startupScene.sceneName.empty()
? ResolveSceneDisplayName(startupScene.scenePath)
: startupScene.sceneName;
m_session->sceneDocumentDirty = false;
m_session->runtimeMode = EditorRuntimeMode::Edit;
ApplyStartupSceneDocument(startupScene);
SetRuntimeMode(EditorRuntimeMode::Edit);
CaptureCleanSceneRevision();
}
@@ -82,12 +84,15 @@ void EditorRuntimeCoordinator::Shutdown() {
m_runtimeLoop.Stop();
m_playSession.reset();
if (m_session != nullptr) {
m_session->runtimeMode = EditorRuntimeMode::Edit;
m_sceneDocument = {};
SyncSessionDocumentProjection();
SetRuntimeMode(EditorRuntimeMode::Edit);
}
m_session = nullptr;
m_sceneRuntime = nullptr;
m_projectRuntime = nullptr;
m_runtimePaths = EditorRuntimePaths{};
m_sceneDocument = {};
m_lastFrameTickTime = {};
m_lastCleanSceneContentRevision = 0u;
m_lastObservedSceneContentRevision = 0u;
@@ -116,16 +121,16 @@ void EditorRuntimeCoordinator::Tick(float deltaSeconds) {
SyncSceneDocumentDirtyFromRevision();
if (!m_runtimeLoop.IsRunning()) {
if (m_session->runtimeMode != EditorRuntimeMode::Edit) {
m_session->runtimeMode = EditorRuntimeMode::Edit;
SetRuntimeMode(EditorRuntimeMode::Edit);
}
return;
}
m_runtimeLoop.Tick(deltaSeconds);
if (m_runtimeLoop.IsPaused()) {
m_session->runtimeMode = EditorRuntimeMode::Paused;
SetRuntimeMode(EditorRuntimeMode::Paused);
} else {
m_session->runtimeMode = EditorRuntimeMode::Play;
SetRuntimeMode(EditorRuntimeMode::Play);
}
}
@@ -147,10 +152,13 @@ bool EditorRuntimeCoordinator::RequestOpenSceneAsset(
return false;
}
m_session->currentScenePath = scenePath;
m_session->currentSceneName = ResolveSceneDisplayName(scenePath);
m_session->sceneDocumentDirty = false;
m_sceneDocument.ready = true;
m_sceneDocument.loadedFromDisk = true;
m_sceneDocument.currentPath = scenePath;
m_sceneDocument.currentName = ResolveActiveSceneDisplayName(scenePath);
m_sceneDocument.dirty = false;
CaptureCleanSceneRevision();
SyncSessionDocumentProjection();
SetLastMessage("Opened scene: " + scenePath.string());
return true;
}
@@ -182,7 +190,7 @@ EditorRuntimeCoordinator::EvaluateFileCommand(
if (!HasActiveScene()) {
return BuildDisabledResult("No active scene to save.");
}
if (m_session->currentScenePath.empty()) {
if (m_sceneDocument.currentPath.empty()) {
return BuildDisabledResult(
"Save Scene As is required before saving this scene.");
}
@@ -215,28 +223,30 @@ EditorRuntimeCoordinator::DispatchFileCommand(
return BuildRejectedDispatch(m_lastMessage);
}
const EditorStartupSceneResult& startupScene =
m_sceneRuntime->GetStartupResult();
m_session->currentScenePath = std::filesystem::path();
m_session->currentSceneName = startupScene.sceneName.empty()
? std::string("Untitled")
: startupScene.sceneName;
m_session->sceneDocumentDirty = true;
m_sceneDocument.ready = true;
m_sceneDocument.loadedFromDisk = false;
m_sceneDocument.currentPath.clear();
m_sceneDocument.currentName = ResolveActiveSceneDisplayName();
m_sceneDocument.dirty = true;
CaptureCleanSceneRevision();
SyncSessionDocumentProjection();
SetLastMessage("New scene created.");
return BuildExecutedDispatch(m_lastMessage);
}
if (commandId == "file.save_scene") {
const std::filesystem::path scenePath = m_session->currentScenePath;
const std::filesystem::path scenePath = m_sceneDocument.currentPath;
if (!m_sceneRuntime->SaveScene(scenePath)) {
SetLastMessage("Failed to save scene: " + scenePath.string());
return BuildRejectedDispatch(m_lastMessage);
}
m_session->sceneDocumentDirty = false;
m_session->currentSceneName = ResolveSceneDisplayName(scenePath);
m_sceneDocument.ready = true;
m_sceneDocument.loadedFromDisk = !scenePath.empty();
m_sceneDocument.currentName = ResolveActiveSceneDisplayName(scenePath);
m_sceneDocument.dirty = false;
CaptureCleanSceneRevision();
SyncSessionDocumentProjection();
SetLastMessage("Scene saved: " + scenePath.string());
return BuildExecutedDispatch(m_lastMessage);
}
@@ -408,9 +418,8 @@ bool EditorRuntimeCoordinator::StartPlayMode() {
if (!EnsurePlaySession()) {
return false;
}
m_runtimeLoop.Start(m_playSession->GetRuntimeScene());
m_session->runtimeMode = EditorRuntimeMode::Play;
SetRuntimeMode(EditorRuntimeMode::Play);
m_lastFrameTickTime = std::chrono::steady_clock::now();
return m_runtimeLoop.IsRunning();
}
@@ -423,7 +432,7 @@ bool EditorRuntimeCoordinator::StopPlayMode() {
m_runtimeLoop.Stop();
m_playSession.reset();
m_sceneRuntime->RefreshScene();
m_session->runtimeMode = EditorRuntimeMode::Edit;
SetRuntimeMode(EditorRuntimeMode::Edit);
return true;
}
@@ -433,7 +442,7 @@ bool EditorRuntimeCoordinator::PausePlayMode() {
}
m_runtimeLoop.Pause();
m_session->runtimeMode = EditorRuntimeMode::Paused;
SetRuntimeMode(EditorRuntimeMode::Paused);
return true;
}
@@ -443,7 +452,7 @@ bool EditorRuntimeCoordinator::ResumePlayMode() {
}
m_runtimeLoop.Resume();
m_session->runtimeMode = EditorRuntimeMode::Play;
SetRuntimeMode(EditorRuntimeMode::Play);
m_lastFrameTickTime = std::chrono::steady_clock::now();
return true;
}
@@ -457,17 +466,52 @@ bool EditorRuntimeCoordinator::StepPlayMode() {
if (!EnsurePlaySession()) {
return false;
}
m_runtimeLoop.Start(m_playSession->GetRuntimeScene());
}
m_runtimeLoop.StepFrame();
m_runtimeLoop.Tick(m_runtimeLoop.GetSettings().fixedDeltaTime);
m_session->runtimeMode = EditorRuntimeMode::Paused;
SetRuntimeMode(EditorRuntimeMode::Paused);
m_lastFrameTickTime = std::chrono::steady_clock::now();
return true;
}
void EditorRuntimeCoordinator::ApplyStartupSceneDocument(
const EditorStartupSceneResult& startupScene) {
m_sceneDocument.ready = startupScene.ready;
m_sceneDocument.loadedFromDisk = startupScene.loadedFromDisk;
m_sceneDocument.currentPath = startupScene.scenePath;
m_sceneDocument.currentName =
ResolveDocumentSceneName(startupScene.sceneName, startupScene.scenePath);
m_sceneDocument.dirty = false;
SyncSessionDocumentProjection();
}
std::string EditorRuntimeCoordinator::ResolveActiveSceneDisplayName(
const std::filesystem::path& fallbackPath) const {
const std::filesystem::path& scenePath =
fallbackPath.empty() ? m_sceneDocument.currentPath : fallbackPath;
const std::string sceneName =
IsReady() ? m_sceneRuntime->GetActiveSceneName() : std::string();
return ResolveDocumentSceneName(sceneName, scenePath);
}
void EditorRuntimeCoordinator::SyncSessionDocumentProjection() {
if (m_session == nullptr) {
return;
}
m_session->currentScenePath = m_sceneDocument.currentPath;
m_session->currentSceneName = m_sceneDocument.currentName;
m_session->sceneDocumentDirty = m_sceneDocument.dirty;
}
void EditorRuntimeCoordinator::SetRuntimeMode(EditorRuntimeMode mode) {
if (m_session != nullptr) {
m_session->runtimeMode = mode;
}
}
void EditorRuntimeCoordinator::SyncSceneDocumentDirtyFromRevision() {
if (!IsReady()) {
return;
@@ -478,7 +522,8 @@ void EditorRuntimeCoordinator::SyncSceneDocumentDirtyFromRevision() {
m_lastObservedSceneContentRevision = revision;
}
if (revision != m_lastCleanSceneContentRevision) {
m_session->sceneDocumentDirty = true;
m_sceneDocument.dirty = true;
SyncSessionDocumentProjection();
}
}

View File

@@ -17,6 +17,7 @@ namespace XCEngine::UI::Editor::App {
class EditorProjectRuntime;
class EditorScenePlaySession;
class EditorSceneRuntime;
struct EditorStartupSceneResult;
struct EditorSession;
class EditorRuntimeCoordinator final
@@ -31,7 +32,8 @@ public:
EditorSession& session,
EditorSceneRuntime& sceneRuntime,
EditorProjectRuntime& projectRuntime,
const EditorRuntimePaths& runtimePaths);
const EditorRuntimePaths& runtimePaths,
const EditorStartupSceneResult& startupScene);
void Shutdown();
void TickFrame();
@@ -54,6 +56,14 @@ public:
std::string_view commandId) override;
private:
struct SceneDocumentState {
bool ready = false;
bool loadedFromDisk = false;
std::filesystem::path currentPath = {};
std::string currentName = {};
bool dirty = false;
};
bool IsReady() const;
bool HasActiveScene() const;
bool IsPlayModeActive() const;
@@ -63,6 +73,11 @@ private:
bool PausePlayMode();
bool ResumePlayMode();
bool StepPlayMode();
void ApplyStartupSceneDocument(const EditorStartupSceneResult& startupScene);
std::string ResolveActiveSceneDisplayName(
const std::filesystem::path& fallbackPath = {}) const;
void SyncSessionDocumentProjection();
void SetRuntimeMode(EditorRuntimeMode mode);
void SyncSceneDocumentDirtyFromRevision();
void CaptureCleanSceneRevision();
void SetLastMessage(std::string message);
@@ -71,6 +86,7 @@ private:
EditorSceneRuntime* m_sceneRuntime = nullptr;
EditorProjectRuntime* m_projectRuntime = nullptr;
EditorRuntimePaths m_runtimePaths = {};
SceneDocumentState m_sceneDocument = {};
::XCEngine::Components::RuntimeLoop m_runtimeLoop{
::XCEngine::Components::RuntimeLoop::Settings{} };
std::unique_ptr<EditorScenePlaySession> m_playSession = {};

View File

@@ -103,7 +103,6 @@ std::string_view GetSceneToolInteractionLockName(SceneToolInteractionLock lock)
void EditorSceneRuntime::Reset() {
m_projectRoot.clear();
m_startupSceneResult = {};
m_ownedSelectionService.ClearSelection();
m_selectionService = &m_ownedSelectionService;
ResetTransformEditHistory();
@@ -116,15 +115,17 @@ void EditorSceneRuntime::SetBackend(std::unique_ptr<EditorSceneBackend> backend)
Reset();
}
bool EditorSceneRuntime::Initialize(const std::filesystem::path& projectRoot) {
EditorStartupSceneResult EditorSceneRuntime::Initialize(
const std::filesystem::path& projectRoot) {
Reset();
if (m_backend == nullptr) {
return false;
return {};
}
m_projectRoot = projectRoot;
m_startupSceneResult = m_backend->EnsureStartupScene(projectRoot);
const EditorStartupSceneResult startupScene =
m_backend->EnsureStartupScene(projectRoot);
RefreshScene();
return m_startupSceneResult.ready;
return startupScene;
}
void EditorSceneRuntime::BindSelectionService(
@@ -152,10 +153,6 @@ void EditorSceneRuntime::EnsureSceneSelection() {
SelectFirstAvailableGameObject();
}
const EditorStartupSceneResult& EditorSceneRuntime::GetStartupResult() const {
return m_startupSceneResult;
}
EditorSceneHierarchySnapshot EditorSceneRuntime::BuildHierarchySnapshot() const {
return m_backend != nullptr
? m_backend->BuildHierarchySnapshot()
@@ -284,18 +281,6 @@ bool EditorSceneRuntime::NewScene(std::string_view sceneName) {
return false;
}
m_startupSceneResult.ready = true;
m_startupSceneResult.loadedFromDisk = false;
m_startupSceneResult.scenePath = std::filesystem::path();
if (::XCEngine::Components::Scene* activeScene = m_backend->GetActiveScene();
activeScene != nullptr) {
m_startupSceneResult.sceneName = activeScene->GetName();
} else {
m_startupSceneResult.sceneName = sceneName.empty()
? std::string("Untitled")
: std::string(sceneName);
}
ResetTransformEditHistory();
SelectionService().ClearSelection();
IncrementInspectorRevision();
@@ -310,11 +295,6 @@ bool EditorSceneRuntime::OpenSceneAsset(const std::filesystem::path& scenePath)
return false;
}
m_startupSceneResult.ready = true;
m_startupSceneResult.loadedFromDisk = true;
m_startupSceneResult.scenePath = scenePath;
m_startupSceneResult.sceneName = scenePath.stem().string();
ResetTransformEditHistory();
SelectionService().ClearSelection();
IncrementInspectorRevision();
@@ -332,6 +312,15 @@ bool EditorSceneRuntime::SaveScene(const std::filesystem::path& scenePath) {
return m_backend != nullptr ? m_backend->GetActiveScene() : nullptr;
}
std::string EditorSceneRuntime::GetActiveSceneName() const {
if (const ::XCEngine::Components::Scene* activeScene = GetActiveScene();
activeScene != nullptr) {
return activeScene->GetName();
}
return {};
}
std::unique_ptr<EditorScenePlaySession> EditorSceneRuntime::BeginPlaySession() {
return m_backend != nullptr
? m_backend->BeginPlaySession()

View File

@@ -38,13 +38,12 @@ public:
void Reset();
void SetBackend(std::unique_ptr<EditorSceneBackend> backend);
bool Initialize(const std::filesystem::path& projectRoot);
EditorStartupSceneResult Initialize(const std::filesystem::path& projectRoot);
void BindSelectionService(EditorSelectionService* selectionService);
void RefreshScene();
void EnsureSceneSelection();
const EditorStartupSceneResult& GetStartupResult() const;
EditorSceneHierarchySnapshot BuildHierarchySnapshot() const;
std::vector<EditorSceneViewportIconSnapshot> BuildSceneViewportIconSnapshots() const;
std::optional<EditorSceneViewportSelectionSnapshot> BuildSceneViewportSelectionSnapshot() const;
@@ -67,6 +66,7 @@ public:
bool OpenSceneAsset(const std::filesystem::path& scenePath);
bool SaveScene(const std::filesystem::path& scenePath);
::XCEngine::Components::Scene* GetActiveScene() const;
std::string GetActiveSceneName() const;
std::unique_ptr<EditorScenePlaySession> BeginPlaySession();
bool RenameGameObject(
@@ -139,7 +139,6 @@ private:
std::filesystem::path m_projectRoot = {};
std::unique_ptr<EditorSceneBackend> m_backend = {};
EditorStartupSceneResult m_startupSceneResult = {};
EditorSelectionService m_ownedSelectionService = {};
EditorSelectionService* m_selectionService = &m_ownedSelectionService;
std::vector<TransformEditTransaction> m_transformUndoStack = {};

View File

@@ -62,7 +62,13 @@ public:
return true;
}
bool OpenSceneAsset(const std::filesystem::path&) override {
bool OpenSceneAsset(const std::filesystem::path& scenePath) override {
lastOpenedScenePath = scenePath;
if (!openSceneResult) {
return false;
}
editScene = std::make_unique<Scene>(openedSceneName);
activeScene = editScene.get();
return true;
}
@@ -142,11 +148,14 @@ public:
std::unique_ptr<Scene> editScene = {};
std::unique_ptr<Scene> runtimeScene = {};
Scene* activeScene = nullptr;
std::filesystem::path lastOpenedScenePath = {};
int beginPlaySessionCallCount = 0;
int endPlaySessionCallCount = 0;
int saveActiveSceneCallCount = 0;
bool savedActiveSceneWasEditScene = false;
bool failBeginPlaySession = false;
bool openSceneResult = true;
std::string openedSceneName = "Opened";
};
FakeEditorScenePlaySession::~FakeEditorScenePlaySession() {
@@ -162,12 +171,15 @@ struct RuntimeCoordinatorHarness {
auto backend = std::make_unique<FakeEditorSceneBackend>();
backendPtr = backend.get();
sceneRuntime.SetBackend(std::move(backend));
EXPECT_TRUE(sceneRuntime.Initialize("D:/Project"));
const EditorStartupSceneResult startupScene =
sceneRuntime.Initialize("D:/Project");
EXPECT_TRUE(startupScene.ready);
coordinator.Initialize(
session,
sceneRuntime,
projectRuntime,
EditorRuntimePaths{});
EditorRuntimePaths{},
startupScene);
}
EditorSession session = {};
@@ -177,6 +189,42 @@ struct RuntimeCoordinatorHarness {
EditorRuntimeCoordinator coordinator = {};
};
TEST(EditorRuntimeCoordinatorTests, InitializeProjectsStartupSceneDocumentStateToSession) {
RuntimeCoordinatorHarness harness = {};
EXPECT_EQ(
harness.session.currentScenePath,
std::filesystem::path("D:/Project/Assets/Scenes/Main.xc"));
EXPECT_EQ(harness.session.currentSceneName, "Edit");
EXPECT_FALSE(harness.session.sceneDocumentDirty);
EXPECT_EQ(harness.session.runtimeMode, EditorRuntimeMode::Edit);
}
TEST(EditorRuntimeCoordinatorTests, NewSceneProjectsUnsavedDirtyDocumentStateToSession) {
RuntimeCoordinatorHarness harness = {};
const UIEditorHostCommandDispatchResult newSceneResult =
harness.coordinator.DispatchFileCommand("file.new_scene");
EXPECT_TRUE(newSceneResult.commandExecuted);
EXPECT_TRUE(harness.session.currentScenePath.empty());
EXPECT_EQ(harness.session.currentSceneName, "Untitled");
EXPECT_TRUE(harness.session.sceneDocumentDirty);
}
TEST(EditorRuntimeCoordinatorTests, OpenSceneProjectsCoordinatorOwnedDocumentStateToSession) {
RuntimeCoordinatorHarness harness = {};
ASSERT_NE(harness.backendPtr, nullptr);
harness.backendPtr->openedSceneName = "Opened Scene";
const std::filesystem::path scenePath =
"D:/Project/Assets/Scenes/Secondary.xc";
ASSERT_TRUE(harness.coordinator.RequestOpenSceneAsset(scenePath));
EXPECT_EQ(harness.backendPtr->lastOpenedScenePath, scenePath);
EXPECT_EQ(harness.session.currentScenePath, scenePath);
EXPECT_EQ(harness.session.currentSceneName, "Opened Scene");
EXPECT_FALSE(harness.session.sceneDocumentDirty);
}
TEST(EditorRuntimeCoordinatorTests, PlayModeRunsRuntimeSceneAndRestoresEditSceneOnStop) {
RuntimeCoordinatorHarness harness = {};
ASSERT_NE(harness.backendPtr, nullptr);

View File

@@ -112,7 +112,9 @@ public:
TEST(EditorSceneRuntimeBackendTests, InitializeFailsWithoutBoundBackend) {
EditorSceneRuntime runtime = {};
EXPECT_FALSE(runtime.Initialize("D:/Xuanchi/Main/XCEngine/project"));
const EditorStartupSceneResult result =
runtime.Initialize("D:/Xuanchi/Main/XCEngine/project");
EXPECT_FALSE(result.ready);
}
TEST(EditorSceneRuntimeBackendTests, InitializeUsesBoundBackend) {
@@ -124,12 +126,14 @@ TEST(EditorSceneRuntimeBackendTests, InitializeUsesBoundBackend) {
EditorSceneRuntime runtime = {};
runtime.SetBackend(std::move(backend));
EXPECT_TRUE(runtime.Initialize("D:/Xuanchi/Main/XCEngine/project"));
const EditorStartupSceneResult result =
runtime.Initialize("D:/Xuanchi/Main/XCEngine/project");
EXPECT_TRUE(result.ready);
EXPECT_EQ(backendPtr->ensureStartupSceneCallCount, 1);
EXPECT_EQ(
backendPtr->lastProjectRoot,
std::filesystem::path("D:/Xuanchi/Main/XCEngine/project"));
EXPECT_EQ(runtime.GetStartupResult().sceneName, "Main");
EXPECT_EQ(result.sceneName, "Main");
}
TEST(EditorSceneRuntimeBackendTests, SetSelectionUsesBoundBackendObjectSnapshotLookup) {