diff --git a/Assets/Tests/Editor/CompileResponseFactoryTests.cs b/Assets/Tests/Editor/CompileResponseFactoryTests.cs
index 24cf2b3e2b..a3ac89f1e2 100644
--- a/Assets/Tests/Editor/CompileResponseFactoryTests.cs
+++ b/Assets/Tests/Editor/CompileResponseFactoryTests.cs
@@ -110,6 +110,48 @@ public void CreateResponse_WhenForceCompileHasOutcome_ExplainsNullDetails()
Assert.That(response.Message, Is.EqualTo(ForceCompileUnknownResult.MessageText));
}
+ [Test]
+ public void CreateResponse_WhenIndeterminateNonForceCompileHasCounts_PreservesCountsWithoutIssues()
+ {
+ // Verifies indeterminate non-force results keep observed counts while withholding unreliable issue lists.
+ CompilerMessage error = new CompilerMessage
+ {
+ type = CompilerMessageType.Error,
+ message = "error",
+ file = "Assets/Test.cs",
+ line = 12
+ };
+ CompilerMessage warning = new CompilerMessage
+ {
+ type = CompilerMessageType.Warning,
+ message = "warning",
+ file = "Assets/Test.cs",
+ line = 15
+ };
+ CompileResult result = new CompileResult(
+ success: null,
+ errorCount: 1,
+ warningCount: 1,
+ completedAt: DateTime.Now,
+ messages: new[] { error, warning },
+ errors: new[] { error },
+ warnings: new[] { warning },
+ isIndeterminate: true,
+ message: null);
+
+ CompileResponse response =
+ CompileResponseFactory.CreateResponse(result, forceRecompile: false);
+
+ Assert.That(response.Success, Is.Null);
+ Assert.That(response.ErrorCount, Is.EqualTo(1));
+ Assert.That(response.WarningCount, Is.EqualTo(1));
+ Assert.That(response.Errors, Is.Null);
+ Assert.That(response.Warnings, Is.Null);
+ Assert.That(
+ response.Message,
+ Is.EqualTo("Compilation status is unknown. Use get-logs to inspect the compiler output."));
+ }
+
[Test]
public void CreateResponse_WhenForceCompileHasPreservedFailure_MapsDetailedIssues()
{
diff --git a/Assets/Tests/Editor/CompileResultSessionRecorderTests.cs b/Assets/Tests/Editor/CompileResultSessionRecorderTests.cs
new file mode 100644
index 0000000000..b70ab59283
--- /dev/null
+++ b/Assets/Tests/Editor/CompileResultSessionRecorderTests.cs
@@ -0,0 +1,79 @@
+using System;
+using NUnit.Framework;
+using UnityEditor.Compilation;
+
+using io.github.hatayama.UnityCliLoop.Domain;
+using io.github.hatayama.UnityCliLoop.FirstPartyTools;
+using io.github.hatayama.UnityCliLoop.Infrastructure;
+
+namespace io.github.hatayama.UnityCliLoop.Tests.Editor
+{
+ ///
+ /// Tests the single compile result recording seam used by delayed CLI polling.
+ ///
+ [TestFixture]
+ public sealed class CompileResultSessionRecorderTests
+ {
+ [Test]
+ public void RecordCompileResult_WhenPendingRequestExists_StoresResponseAndClearsPendingRequest()
+ {
+ // Verifies raw compile results are shaped and persisted through the recorder seam once.
+ UnityCliLoopCompileResultSessionRepository compileResultSessionRepository =
+ UnityCliLoopEditorSessionStateTestFactory.CreateCompileResultSessionRepository();
+ UnityCliLoopPendingCompileSessionRepository pendingCompileSessionRepository =
+ UnityCliLoopEditorSessionStateTestFactory.CreatePendingCompileSessionRepository();
+ UnityCliLoopCompileSessionLifecycleService compileSessionLifecycleService =
+ UnityCliLoopEditorSessionStateTestFactory.CreateCompileSessionLifecycleService();
+ UnityCliLoopEditorSessionStateSnapshot originalSnapshot =
+ UnityCliLoopEditorSessionStateTestFactory.CaptureSnapshot();
+ UnityCliLoopEditorSessionStateTestFactory.ClearAll();
+
+ try
+ {
+ compileSessionLifecycleService.MarkPendingCompileRequest(
+ "compile_test_request",
+ forceRecompile: false,
+ markedAtUtc: DateTime.UtcNow);
+ CompilerMessage warning = new CompilerMessage
+ {
+ type = CompilerMessageType.Warning,
+ message = "warning",
+ file = "Assets/Test.cs",
+ line = 15
+ };
+ CompileResult result = new CompileResult(
+ success: true,
+ errorCount: 0,
+ warningCount: 1,
+ completedAt: DateTime.Now,
+ messages: new[] { warning },
+ errors: Array.Empty(),
+ warnings: new[] { warning });
+
+ CompileResponse response = CompileResultSessionRecorder.RecordCompileResult(
+ compileResultSessionRepository,
+ pendingCompileSessionRepository,
+ "compile_test_request",
+ forceRecompile: false,
+ result,
+ "compile_test_request");
+
+ UnityCliLoopStoredCompileResult storedResult =
+ compileResultSessionRepository.GetCompileResult("compile_test_request");
+ UnityCliLoopPendingCompileRequest pendingRequest =
+ UnityCliLoopEditorSessionStateTestFactory.GetSinglePendingCompileRequest(
+ pendingCompileSessionRepository);
+
+ Assert.That(response.Success, Is.True);
+ Assert.That(response.WarningCount, Is.EqualTo(1));
+ Assert.That(storedResult.ResultJson, Does.Contain("\"Success\":true"));
+ Assert.That(storedResult.ResultJson, Does.Contain("\"Warnings\":["));
+ Assert.That(pendingRequest.HasRequest, Is.False);
+ }
+ finally
+ {
+ originalSnapshot.Restore();
+ }
+ }
+ }
+}
diff --git a/Assets/Tests/Editor/CompileResultSessionRecorderTests.cs.meta b/Assets/Tests/Editor/CompileResultSessionRecorderTests.cs.meta
new file mode 100644
index 0000000000..37fd4bdc69
--- /dev/null
+++ b/Assets/Tests/Editor/CompileResultSessionRecorderTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b82c193eb8b44b78b9ecd8a80be693aa
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Tests/Editor/CompileSessionResultStoreTests.cs b/Assets/Tests/Editor/CompileSessionResultStoreTests.cs
index 1d5972f722..ef0938d60a 100644
--- a/Assets/Tests/Editor/CompileSessionResultStoreTests.cs
+++ b/Assets/Tests/Editor/CompileSessionResultStoreTests.cs
@@ -1,5 +1,6 @@
using System;
using NUnit.Framework;
+using UnityEditor.Compilation;
using io.github.hatayama.UnityCliLoop.Domain;
using io.github.hatayama.UnityCliLoop.FirstPartyTools;
@@ -65,5 +66,68 @@ public void StoreCompileResult_WhenResultIsPersisted_UsesPascalCaseJson()
originalSnapshot.Restore();
}
}
+
+ [Test]
+ public void StoreCompileResult_WhenEquivalentNormalResponsesAreStoredTwice_WritesByteIdenticalJson()
+ {
+ // Verifies equivalent normal responses serialize to stable bytes across repeated storage calls.
+ UnityCliLoopCompileResultSessionRepository compileResultSessionRepository =
+ UnityCliLoopEditorSessionStateTestFactory.CreateCompileResultSessionRepository();
+ UnityCliLoopPendingCompileSessionRepository pendingCompileSessionRepository =
+ UnityCliLoopEditorSessionStateTestFactory.CreatePendingCompileSessionRepository();
+ UnityCliLoopEditorSessionStateSnapshot originalSnapshot =
+ UnityCliLoopEditorSessionStateTestFactory.CaptureSnapshot();
+ UnityCliLoopEditorSessionStateTestFactory.ClearAll();
+
+ try
+ {
+ CompilerMessage warning = new CompilerMessage
+ {
+ type = CompilerMessageType.Warning,
+ message = "warning",
+ file = "Assets/Test.cs",
+ line = 15
+ };
+ CompileResult result = new CompileResult(
+ success: true,
+ errorCount: 0,
+ warningCount: 1,
+ completedAt: DateTime.Now,
+ messages: new[] { warning },
+ errors: Array.Empty(),
+ warnings: new[] { warning });
+ CompileResponse firstResponse =
+ CompileResponseFactory.CreateResponse(result, forceRecompile: false);
+ CompileResponse secondResponse =
+ CompileResponseFactory.CreateResponse(result, forceRecompile: false);
+
+ CompileSessionResultStore.StoreCompileResult(
+ compileResultSessionRepository,
+ pendingCompileSessionRepository,
+ "compile_test_request",
+ forceRecompile: false,
+ firstResponse,
+ "compile_test_request");
+ string firstJson =
+ compileResultSessionRepository.GetCompileResult("compile_test_request").ResultJson;
+
+ CompileSessionResultStore.StoreCompileResult(
+ compileResultSessionRepository,
+ pendingCompileSessionRepository,
+ "compile_test_request",
+ forceRecompile: false,
+ secondResponse,
+ "compile_test_request");
+ string secondJson =
+ compileResultSessionRepository.GetCompileResult("compile_test_request").ResultJson;
+
+ Assert.That(firstJson, Is.EqualTo(secondJson));
+ Assert.That(firstJson, Does.Contain("\"ProjectRoot\":"));
+ }
+ finally
+ {
+ originalSnapshot.Restore();
+ }
+ }
}
}
diff --git a/Assets/Tests/Editor/CompileUseCaseTests.cs b/Assets/Tests/Editor/CompileUseCaseTests.cs
new file mode 100644
index 0000000000..4c7970c3e5
--- /dev/null
+++ b/Assets/Tests/Editor/CompileUseCaseTests.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using UnityEditor.Compilation;
+
+using io.github.hatayama.UnityCliLoop.Domain;
+using io.github.hatayama.UnityCliLoop.FirstPartyTools;
+using io.github.hatayama.UnityCliLoop.Infrastructure;
+
+namespace io.github.hatayama.UnityCliLoop.Tests.Editor
+{
+ ///
+ /// Tests compile use case orchestration around delayed result persistence.
+ ///
+ [TestFixture]
+ public sealed class CompileUseCaseTests
+ {
+ [Test]
+ public async Task CompileAsync_WhenExecutionLayerStoresDelayedSuccess_DoesNotStoreResultAgain()
+ {
+ // Verifies the UseCase success path preserves the execution layer's single delayed-result write.
+ UnityCliLoopCompileResultSessionRepository innerCompileResultSessionRepository =
+ UnityCliLoopEditorSessionStateTestFactory.CreateCompileResultSessionRepository();
+ CountingCompileResultSessionRepository compileResultSessionRepository =
+ new(innerCompileResultSessionRepository);
+ UnityCliLoopPendingCompileSessionRepository pendingCompileSessionRepository =
+ UnityCliLoopEditorSessionStateTestFactory.CreatePendingCompileSessionRepository();
+ UnityCliLoopCompileSessionLifecycleService compileSessionLifecycleService =
+ new(
+ UnityCliLoopEditorSessionStateTestFactory.CreateSessionFlagsRepository(),
+ compileResultSessionRepository,
+ pendingCompileSessionRepository);
+ UnityCliLoopEditorSessionStateSnapshot originalSnapshot =
+ UnityCliLoopEditorSessionStateTestFactory.CaptureSnapshot();
+ UnityCliLoopEditorSessionStateTestFactory.ClearAll();
+
+ try
+ {
+ CompileSchema request = new()
+ {
+ WaitForDomainReload = true,
+ RequestId = "compile_test_request",
+ ForceRecompile = false,
+ ReloadExternalSceneChanges = true
+ };
+ CompileResult executionResult = CreateSuccessfulCompileResult();
+ CompileUseCase useCase = new(
+ compileSessionLifecycleService,
+ compileResultSessionRepository,
+ pendingCompileSessionRepository);
+ useCase.SetCompilationExecutionForTesting((compileRequest, ct) =>
+ {
+ ct.ThrowIfCancellationRequested();
+ CompileResultSessionRecorder.RecordCompileResult(
+ compileResultSessionRepository,
+ pendingCompileSessionRepository,
+ compileRequest.RequestId,
+ compileRequest.ForceRecompile,
+ executionResult,
+ compileRequest.RequestId);
+ return Task.FromResult(executionResult);
+ });
+
+ CompileResponse response = await useCase.CompileAsync(request, CancellationToken.None);
+
+ Assert.That(compileResultSessionRepository.StoreCount, Is.EqualTo(1));
+ Assert.That(response.Success, Is.True);
+ Assert.That(response.ProjectRoot, Is.Not.Empty);
+ UnityCliLoopStoredCompileResult storedResult =
+ compileResultSessionRepository.GetCompileResult("compile_test_request");
+ Assert.That(storedResult.ResultJson, Does.Contain("\"ProjectRoot\":"));
+ }
+ finally
+ {
+ originalSnapshot.Restore();
+ }
+ }
+
+ private static CompileResult CreateSuccessfulCompileResult()
+ {
+ CompilerMessage warning = new()
+ {
+ type = CompilerMessageType.Warning,
+ message = "warning",
+ file = "Assets/Test.cs",
+ line = 15
+ };
+ return new CompileResult(
+ success: true,
+ errorCount: 0,
+ warningCount: 1,
+ completedAt: DateTime.Now,
+ messages: new[] { warning },
+ errors: Array.Empty(),
+ warnings: new[] { warning });
+ }
+
+ private sealed class CountingCompileResultSessionRepository : ICompileResultSessionRepository
+ {
+ private readonly ICompileResultSessionRepository _inner;
+
+ internal CountingCompileResultSessionRepository(ICompileResultSessionRepository inner)
+ {
+ Assert.That(inner, Is.Not.Null);
+ _inner = inner;
+ }
+
+ internal int StoreCount { get; private set; }
+
+ public void StoreCompileResult(
+ string requestId,
+ bool forceRecompile,
+ string resultJson,
+ DateTime completedAtUtc)
+ {
+ StoreCount++;
+ _inner.StoreCompileResult(requestId, forceRecompile, resultJson, completedAtUtc);
+ }
+
+ public UnityCliLoopStoredCompileResult GetCompileResult(string requestId)
+ {
+ return _inner.GetCompileResult(requestId);
+ }
+
+ public UnityCliLoopStoredCompileResult GetStoredCompileResult()
+ {
+ return _inner.GetStoredCompileResult();
+ }
+
+ public UnityCliLoopStoredCompileResult[] GetStoredCompileResults()
+ {
+ return _inner.GetStoredCompileResults();
+ }
+
+ public void ClearCompileResult()
+ {
+ _inner.ClearCompileResult();
+ }
+
+ public bool ClearExpiredCompileResult(DateTime utcNow, TimeSpan lifetime)
+ {
+ return _inner.ClearExpiredCompileResult(utcNow, lifetime);
+ }
+ }
+ }
+}
diff --git a/Assets/Tests/Editor/CompileUseCaseTests.cs.meta b/Assets/Tests/Editor/CompileUseCaseTests.cs.meta
new file mode 100644
index 0000000000..858500f3c7
--- /dev/null
+++ b/Assets/Tests/Editor/CompileUseCaseTests.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1db098aa67194612a97e96410ff1b1e9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/Compile/CompileController.cs b/Packages/src/Editor/FirstPartyTools/Compile/CompileController.cs
index 9ebcd3b52f..df8c267b45 100644
--- a/Packages/src/Editor/FirstPartyTools/Compile/CompileController.cs
+++ b/Packages/src/Editor/FirstPartyTools/Compile/CompileController.cs
@@ -118,7 +118,7 @@ public async Task TryCompileAsync(bool forceRecompile, Cancellati
"compile_controller_validation_failed",
validation.ErrorMessage,
new { force_recompile = forceRecompile });
- return new CompileResult(
+ CompileResult result = new CompileResult(
success: false,
errorCount: 0,
warningCount: 0,
@@ -129,6 +129,13 @@ public async Task TryCompileAsync(bool forceRecompile, Cancellati
isIndeterminate: true,
message: validation.ErrorMessage
);
+ RecordCompileResultIfNeeded(
+ result,
+ BuildCompileControllerStateContext(new Dictionary
+ {
+ ["validation_failed_before_request"] = true
+ }));
+ return result;
}
(bool CanProceed, string Message, string[] ScenePaths) sceneChangeResult =
@@ -143,7 +150,15 @@ public async Task TryCompileAsync(bool forceRecompile, Cancellati
reload_external_scene_changes = _reloadExternalSceneChanges,
scene_paths = sceneChangeResult.ScenePaths
});
- return CompileResultFactory.CreateExternalSceneChangeFailureResult(sceneChangeResult);
+ CompileResult result =
+ CompileResultFactory.CreateExternalSceneChangeFailureResult(sceneChangeResult);
+ RecordCompileResultIfNeeded(
+ result,
+ BuildCompileControllerStateContext(new Dictionary
+ {
+ ["external_scene_change_failed_before_request"] = true
+ }));
+ return result;
}
_isCompiling = true;
@@ -523,14 +538,12 @@ private void RecordCompileResultIfNeeded(
return;
}
- CompileResponse response =
- CompileResponseFactory.CreateResponse(result, _resultRecordingContext.ForceRecompile);
- CompileSessionResultStore.StoreCompileResult(
+ CompileResultSessionRecorder.RecordCompileResult(
_compileResultSessionRepository,
_pendingCompileSessionRepository,
_resultRecordingContext.RequestId,
_resultRecordingContext.ForceRecompile,
- response,
+ result,
_resultRecordingContext.RequestId);
completionContext["result_recorded_in_session_state"] = true;
completionContext["request_id"] = _resultRecordingContext.RequestId;
diff --git a/Packages/src/Editor/FirstPartyTools/Compile/CompileResultRecordingContext.cs b/Packages/src/Editor/FirstPartyTools/Compile/CompileResultRecordingContext.cs
index 38b85c4d3e..59582df48d 100644
--- a/Packages/src/Editor/FirstPartyTools/Compile/CompileResultRecordingContext.cs
+++ b/Packages/src/Editor/FirstPartyTools/Compile/CompileResultRecordingContext.cs
@@ -27,7 +27,7 @@ internal static CompileResultRecordingContext Create(CompileSchema request)
{
Debug.Assert(request != null, "request must not be null");
- if (!request.WaitForDomainReload || string.IsNullOrWhiteSpace(request.RequestId))
+ if (!CanRecord(request))
{
return Disabled();
}
@@ -37,5 +37,11 @@ internal static CompileResultRecordingContext Create(CompileSchema request)
request.RequestId,
request.ForceRecompile);
}
+
+ internal static bool CanRecord(CompileSchema request)
+ {
+ Debug.Assert(request != null, "request must not be null");
+ return request.WaitForDomainReload && !string.IsNullOrWhiteSpace(request.RequestId);
+ }
}
}
diff --git a/Packages/src/Editor/FirstPartyTools/Compile/CompileResultSessionRecorder.cs b/Packages/src/Editor/FirstPartyTools/Compile/CompileResultSessionRecorder.cs
new file mode 100644
index 0000000000..de6034ccf6
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/Compile/CompileResultSessionRecorder.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Diagnostics;
+
+using io.github.hatayama.UnityCliLoop.Domain;
+
+namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
+{
+ ///
+ /// Owns compile result shaping and SessionState persistence for delayed CLI polling.
+ ///
+ internal static class CompileResultSessionRecorder
+ {
+ internal static CompileResponse RecordCompileResult(
+ ICompileResultSessionRepository compileResultSessionRepository,
+ IPendingCompileSessionRepository pendingCompileSessionRepository,
+ string requestId,
+ bool forceRecompile,
+ CompileResult result,
+ string correlationId)
+ {
+ Debug.Assert(compileResultSessionRepository != null, "compileResultSessionRepository must not be null");
+ Debug.Assert(pendingCompileSessionRepository != null, "pendingCompileSessionRepository must not be null");
+ Debug.Assert(!string.IsNullOrWhiteSpace(requestId), "requestId must not be null or whitespace");
+ Debug.Assert(result != null, "result must not be null");
+
+ CompileResponse response = CompileResponseFactory.CreateResponse(result, forceRecompile);
+ return RecordCompileResponse(
+ compileResultSessionRepository,
+ pendingCompileSessionRepository,
+ requestId,
+ forceRecompile,
+ response,
+ correlationId);
+ }
+
+ internal static CompileResponse RecordCompileResponse(
+ ICompileResultSessionRepository compileResultSessionRepository,
+ IPendingCompileSessionRepository pendingCompileSessionRepository,
+ string requestId,
+ bool forceRecompile,
+ CompileResponse response,
+ string correlationId)
+ {
+ Debug.Assert(compileResultSessionRepository != null, "compileResultSessionRepository must not be null");
+ Debug.Assert(pendingCompileSessionRepository != null, "pendingCompileSessionRepository must not be null");
+ Debug.Assert(!string.IsNullOrWhiteSpace(requestId), "requestId must not be null or whitespace");
+ Debug.Assert(response != null, "response must not be null");
+
+ CompileSessionResultStore.StoreCompileResult(
+ compileResultSessionRepository,
+ pendingCompileSessionRepository,
+ requestId,
+ forceRecompile,
+ response,
+ correlationId);
+ return response;
+ }
+ }
+}
diff --git a/Packages/src/Editor/FirstPartyTools/Compile/CompileResultSessionRecorder.cs.meta b/Packages/src/Editor/FirstPartyTools/Compile/CompileResultSessionRecorder.cs.meta
new file mode 100644
index 0000000000..1e6cc21c53
--- /dev/null
+++ b/Packages/src/Editor/FirstPartyTools/Compile/CompileResultSessionRecorder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 402fa4a2acac4d169ece80639e9195bc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Packages/src/Editor/FirstPartyTools/Compile/CompileSessionResultStore.cs b/Packages/src/Editor/FirstPartyTools/Compile/CompileSessionResultStore.cs
index e383721824..280fc4b1f3 100644
--- a/Packages/src/Editor/FirstPartyTools/Compile/CompileSessionResultStore.cs
+++ b/Packages/src/Editor/FirstPartyTools/Compile/CompileSessionResultStore.cs
@@ -29,7 +29,7 @@ internal static void StoreCompileResult(
throw new ArgumentNullException(nameof(compileResultSessionRepository));
IPendingCompileSessionRepository pendingCompileRepository = pendingCompileSessionRepository ??
throw new ArgumentNullException(nameof(pendingCompileSessionRepository));
- result.ProjectRoot = UnityCliLoopPathResolver.GetProjectRoot();
+ StampProjectRoot(result);
string resultJson = JsonConvert.SerializeObject(
result,
Formatting.None,
@@ -63,5 +63,11 @@ internal static void StoreCompileResult(
},
correlationId);
}
+
+ internal static void StampProjectRoot(CompileResponse result)
+ {
+ Debug.Assert(result != null, "result must not be null");
+ result.ProjectRoot = UnityCliLoopPathResolver.GetProjectRoot();
+ }
}
}
diff --git a/Packages/src/Editor/FirstPartyTools/Compile/CompileUseCase.cs b/Packages/src/Editor/FirstPartyTools/Compile/CompileUseCase.cs
index 34fbcff6bd..c66223dabd 100644
--- a/Packages/src/Editor/FirstPartyTools/Compile/CompileUseCase.cs
+++ b/Packages/src/Editor/FirstPartyTools/Compile/CompileUseCase.cs
@@ -21,6 +21,7 @@ public class CompileUseCase
private readonly UnityCliLoopCompileSessionLifecycleService _compileSessionLifecycleService;
private readonly ICompileResultSessionRepository _compileResultSessionRepository;
private readonly IPendingCompileSessionRepository _pendingCompileSessionRepository;
+ private Func> _executeCompilationAsync;
public CompileUseCase(
UnityCliLoopCompileSessionLifecycleService compileSessionLifecycleService,
@@ -37,6 +38,17 @@ public CompileUseCase(
throw new ArgumentNullException(nameof(compileResultSessionRepository));
_pendingCompileSessionRepository = pendingCompileSessionRepository ??
throw new ArgumentNullException(nameof(pendingCompileSessionRepository));
+ _executeCompilationAsync = ExecuteCompilationWithDefaultServiceAsync;
+ }
+
+ ///
+ /// Replaces compilation execution for tests that must not start Unity's real compilation pipeline.
+ ///
+ internal void SetCompilationExecutionForTesting(Func> executeCompilationAsync)
+ {
+ Debug.Assert(executeCompilationAsync != null, "executeCompilationAsync must not be null");
+ _executeCompilationAsync = executeCompilationAsync ??
+ throw new ArgumentNullException(nameof(executeCompilationAsync));
}
///
@@ -79,7 +91,7 @@ public async Task CompileAsync(CompileSchema request, Cancellat
errors: new[] { new CompileIssue(preparation.ErrorMessage, "", 0) },
warnings: Array.Empty());
CompileResponse persistedResponse =
- StoreResponseIfNeeded(request, response, correlationId);
+ StorePreControllerResponseIfNeeded(request, response, correlationId);
return persistedResponse;
}
@@ -110,7 +122,7 @@ public async Task CompileAsync(CompileSchema request, Cancellat
errors: new[] { new CompileIssue("Play Mode did not exit within 5 seconds; compilation aborted.", "", 0) },
warnings: Array.Empty());
CompileResponse persistedResponse =
- StoreResponseIfNeeded(request, response, correlationId);
+ StorePreControllerResponseIfNeeded(request, response, correlationId);
return persistedResponse;
}
}
@@ -133,23 +145,19 @@ public async Task CompileAsync(CompileSchema request, Cancellat
errors: new[] { new CompileIssue(validation.ErrorMessage, "", 0) },
warnings: Array.Empty());
CompileResponse persistedResponse =
- StoreResponseIfNeeded(request, response, correlationId);
+ StorePreControllerResponseIfNeeded(request, response, correlationId);
return persistedResponse;
}
// 3. Compilation execution
ct.ThrowIfCancellationRequested();
- CompilationExecutionService executionService = new(
- _compileResultSessionRepository,
- _pendingCompileSessionRepository);
- CompileResult result = await executionService.ExecuteCompilationAsync(request, ct);
+ CompileResult result = await _executeCompilationAsync(request, ct);
// 4. Result formatting
CompileResponse successResponse =
CompileResponseFactory.CreateResponse(result, request.ForceRecompile);
- CompileResponse persistedSuccessResponse =
- StoreResponseIfNeeded(request, successResponse, correlationId);
- return persistedSuccessResponse;
+ StampProjectRootForDelayedResponseIfNeeded(request, successResponse);
+ return successResponse;
}
private async Task WaitForPlayModeExitAsync(CancellationToken ct)
@@ -183,7 +191,7 @@ private static void PrepareResultStorage(CompileSchema request)
request.RequestId = CreateRequestId();
}
- private CompileResponse StoreResponseIfNeeded(
+ private CompileResponse StorePreControllerResponseIfNeeded(
CompileSchema request,
CompileResponse response,
string correlationId)
@@ -191,22 +199,13 @@ private CompileResponse StoreResponseIfNeeded(
Debug.Assert(request != null, "request must not be null");
Debug.Assert(response != null, "response must not be null");
- if (!request.WaitForDomainReload)
- {
- return response;
- }
-
- if (string.IsNullOrWhiteSpace(request.RequestId))
+ if (!CompileResultRecordingContext.CanRecord(request))
{
- VibeLogger.LogWarning(
- "compile_result_not_stored",
- "Compile result was not stored because the request ID is empty.",
- null,
- correlationId);
+ LogMissingRecordableRequestIfNeeded(request, correlationId);
return response;
}
- CompileSessionResultStore.StoreCompileResult(
+ CompileResultSessionRecorder.RecordCompileResponse(
_compileResultSessionRepository,
_pendingCompileSessionRepository,
request.RequestId,
@@ -216,6 +215,39 @@ private CompileResponse StoreResponseIfNeeded(
return response;
}
+ private void StampProjectRootForDelayedResponseIfNeeded(
+ CompileSchema request,
+ CompileResponse response)
+ {
+ Debug.Assert(request != null, "request must not be null");
+ Debug.Assert(response != null, "response must not be null");
+
+ if (!CompileResultRecordingContext.CanRecord(request))
+ {
+ return;
+ }
+
+ CompileSessionResultStore.StampProjectRoot(response);
+ }
+
+ private static void LogMissingRecordableRequestIfNeeded(
+ CompileSchema request,
+ string correlationId)
+ {
+ Debug.Assert(request != null, "request must not be null");
+
+ if (!request.WaitForDomainReload)
+ {
+ return;
+ }
+
+ VibeLogger.LogWarning(
+ "compile_result_not_stored",
+ "Compile result was not stored because the request ID is empty.",
+ null,
+ correlationId);
+ }
+
private void MarkPendingCompileRequestIfNeeded(
CompileSchema request,
DateTime markedAtUtc,
@@ -304,5 +336,15 @@ private static string CreateRequestId()
string correlationId = UnityCliLoopConstants.GenerateCorrelationId();
return $"compile_{unixTimeMilliseconds}_{correlationId}";
}
+
+ private Task ExecuteCompilationWithDefaultServiceAsync(CompileSchema request, CancellationToken ct)
+ {
+ Debug.Assert(request != null, "request must not be null");
+
+ CompilationExecutionService executionService = new(
+ _compileResultSessionRepository,
+ _pendingCompileSessionRepository);
+ return executionService.ExecuteCompilationAsync(request, ct);
+ }
}
}