Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using NUnit.Framework;

using io.github.hatayama.UnityCliLoop.Domain;
using io.github.hatayama.UnityCliLoop.ToolContracts;

namespace io.github.hatayama.UnityCliLoop.Tests.Editor
{
/// <summary>
/// Tests accepted-request cancellation decisions before infrastructure parses transport details.
/// </summary>
public sealed class JsonRpcAcceptedRequestCancellationPolicyTests
{
[Test]
public void ShouldCancelOnClientDisconnect_WhenMethodIsNotCompile_ReturnsTrue()
{
// Verifies non-compile accepted work is tied to the CLI connection lifetime.
bool shouldCancel = JsonRpcAcceptedRequestCancellationPolicy.ShouldCancelOnClientDisconnect(
UnityCliLoopConstants.TOOL_NAME_GET_LOGS,
true);

Assert.That(shouldCancel, Is.True);
}

[Test]
public void ShouldCancelOnClientDisconnect_WhenCompileWaitsForDomainReload_ReturnsFalse()
{
// Verifies long compile requests can persist after the CLI connection closes.
bool shouldCancel = JsonRpcAcceptedRequestCancellationPolicy.ShouldCancelOnClientDisconnect(
UnityCliLoopConstants.TOOL_NAME_COMPILE,
true);

Assert.That(shouldCancel, Is.False);
}

[Test]
public void ShouldCancelOnClientDisconnect_WhenCompileDoesNotWaitForDomainReload_ReturnsTrue()
{
// Verifies fire-and-forget compile requests keep the usual disconnect cancellation behavior.
bool shouldCancel = JsonRpcAcceptedRequestCancellationPolicy.ShouldCancelOnClientDisconnect(
UnityCliLoopConstants.TOOL_NAME_COMPILE,
false);

Assert.That(shouldCancel, Is.True);
}

[Test]
public void ShouldCancelOnClientDisconnect_WhenCompileWaitPreferenceIsUnknown_ReturnsFalse()
{
// Verifies missing, null, or non-boolean transport values preserve the compile default wait contract.
bool shouldCancel = JsonRpcAcceptedRequestCancellationPolicy.ShouldCancelOnClientDisconnect(
UnityCliLoopConstants.TOOL_NAME_COMPILE,
null);

Assert.That(shouldCancel, Is.False);
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions Assets/Tests/Editor/JsonRpcProcessorCliVersionGateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,46 @@ public async Task ProcessRequest_WhenCompileOmitsReloadWait_KeepsAcceptedRequest
}
}

[Test]
public async Task ProcessRequest_WhenCompileReloadWaitIsNotBoolean_KeepsAcceptedRequestAliveAfterDisconnect()
{
// Verifies malformed compile reload-wait params preserve the default wait contract.
UnityCliLoopToolRegistrarService previousService = ApplicationRegistrar.Service;
IToolSettingsPort toolSettingsPort = new ToolSettingsRepository();
UnityCliLoopToolRegistrarService service = new(
new EmptyInternalToolNameProvider(),
toolSettingsPort,
new UnityCliLoopToolExecutionService(new NoOpEditorRuntimeStatePort()),
UnityCliLoopToolDiscovery.DiscoverTools);
ApplicationRegistrar.RegisterService(service);
service.RegisterCustomTool(new CompileDispatchPolicyTestTool());

bool cancelOnClientDisconnect = true;
try
{
string response = await JsonRpcProcessor.ProcessRequestWithEarlyResponseAsync(
BuildToolRequestWithParams(
UnityCliLoopConstants.TOOL_NAME_COMPILE,
"{\"WaitForDomainReload\":\"false\"}",
1),
CancellationToken.None,
(_, shouldCancelOnClientDisconnect, _) =>
{
cancelOnClientDisconnect = shouldCancelOnClientDisconnect;
return Task.CompletedTask;
});
JObject parsed = JObject.Parse(response);

Assert.That(parsed["error"], Is.Null);
Assert.That(parsed["result"], Is.Not.Null);
Assert.That(cancelOnClientDisconnect, Is.False);
}
finally
{
ApplicationRegistrar.RegisterService(previousService);
}
}

[Test]
public async Task ProcessRequest_WhenCompileDoesNotWaitForDomainReload_CancelsOnClientDisconnect()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using io.github.hatayama.UnityCliLoop.ToolContracts;

namespace io.github.hatayama.UnityCliLoop.Domain
{
/// <summary>
/// Decides whether accepted JSON-RPC work should be canceled when the CLI disconnects.
/// </summary>
public static class JsonRpcAcceptedRequestCancellationPolicy
{
public static bool ShouldCancelOnClientDisconnect(
string methodName,
bool? compileWaitsForDomainReload)
{
if (methodName != UnityCliLoopConstants.TOOL_NAME_COMPILE)
{
return true;
}

return !CompileRequestWaitsForDomainReload(compileWaitsForDomainReload);
}

private static bool CompileRequestWaitsForDomainReload(bool? compileWaitsForDomainReload)
{
return compileWaitsForDomainReload ?? true;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 8 additions & 10 deletions Packages/src/Editor/Infrastructure/Api/JsonRpcProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,31 +283,29 @@ private static bool ShouldCancelAcceptedRequestOnClientDisconnect(JsonRpcRequest
{
System.Diagnostics.Debug.Assert(request != null, "request must not be null");

if (request.Method != UnityCliLoopConstants.TOOL_NAME_COMPILE)
{
return true;
}

return !CompileRequestWaitsForDomainReload(request.Params);
bool? compileWaitsForDomainReload = ReadCompileRequestWaitsForDomainReload(request.Params);
return JsonRpcAcceptedRequestCancellationPolicy.ShouldCancelOnClientDisconnect(
request.Method,
compileWaitsForDomainReload);
}

private static bool CompileRequestWaitsForDomainReload(JToken paramsToken)
private static bool? ReadCompileRequestWaitsForDomainReload(JToken paramsToken)
{
if (paramsToken is not JObject paramsObject)
{
return true;
return null;
}

JToken waitForDomainReloadToken =
paramsObject.GetValue(WaitForDomainReloadParamName, StringComparison.OrdinalIgnoreCase);
if (waitForDomainReloadToken == null)
{
return true;
return null;
}

if (waitForDomainReloadToken.Type != JTokenType.Boolean)
{
return true;
return null;
}

return waitForDomainReloadToken.Value<bool>();
Expand Down
Loading