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
13 changes: 0 additions & 13 deletions Assets/Tests/Editor/OnionAssemblyDependencyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -337,19 +337,6 @@ public void GetHierarchyUseCase_WhenLoaded_CompilesUnderApplicationAssembly()
Assert.That(useCaseAssemblyName, Does.StartWith(FirstPartyToolsAssemblyNamePrefix));
}

[Test]
public void TestExecutionTypes_WhenLoaded_CompileUnderFirstPartyToolsAssembly()
{
// Tests that bundled test-runner implementation types stay inside the first-party tool assembly.
string serviceAssemblyName = typeof(IUnityCliLoopTestExecutionService).Assembly.GetName().Name;
string requestAssemblyName = typeof(UnityCliLoopTestExecutionRequest).Assembly.GetName().Name;
string resultAssemblyName = typeof(UnityCliLoopTestExecutionResult).Assembly.GetName().Name;

Assert.That(serviceAssemblyName, Does.StartWith(FirstPartyToolsAssemblyNamePrefix));
Assert.That(requestAssemblyName, Does.StartWith(FirstPartyToolsAssemblyNamePrefix));
Assert.That(resultAssemblyName, Does.StartWith(FirstPartyToolsAssemblyNamePrefix));
}

[Test]
public void RunTestsUseCase_WhenLoaded_CompilesUnderApplicationAssembly()
{
Expand Down
24 changes: 12 additions & 12 deletions Assets/Tests/Editor/RunTestsUseCaseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ public async Task ExecuteAsync_WithInvalidExecutionState_ShouldFailFastWithoutRu
validationService,
NoCleanupWait
);
UnityCliLoopTestExecutionRequest parameters = new()
RunTestsSchema parameters = new()
{
TestMode = UnityCliLoopTestMode.EditMode,
SaveBeforeRun = true
};

UnityCliLoopTestExecutionResult response = await useCase.ExecuteAsync(parameters, CancellationToken.None);
RunTestsResponse response = await useCase.ExecuteAsync(parameters, CancellationToken.None);

Assert.That(response.Success, Is.False);
Assert.That(response.Status, Is.EqualTo(RunTestsExecutionStatus.ExecutionFailed));
Expand Down Expand Up @@ -60,12 +60,12 @@ public async Task ExecuteAsync_WithUnknownTestMode_ShouldFailFastWithoutRunningT
validationService,
NoCleanupWait
);
UnityCliLoopTestExecutionRequest parameters = new()
RunTestsSchema parameters = new()
{
TestMode = (UnityCliLoopTestMode)999
};

UnityCliLoopTestExecutionResult response = await useCase.ExecuteAsync(parameters, CancellationToken.None);
RunTestsResponse response = await useCase.ExecuteAsync(parameters, CancellationToken.None);

Assert.That(response.Success, Is.False);
Assert.That(response.Message, Does.Contain("Unsupported test mode"));
Expand All @@ -85,7 +85,7 @@ public async Task ExecuteAsync_WithDefaultRequest_ShouldSaveBeforeRun()
validationService,
NoCleanupWait
);
UnityCliLoopTestExecutionRequest parameters = new();
RunTestsSchema parameters = new();

await useCase.ExecuteAsync(parameters, CancellationToken.None);

Expand All @@ -109,13 +109,13 @@ public async Task ExecuteAsync_WhenTestFrameworkUnavailable_ShouldFailFastWithou
validationService,
NoCleanupWait
);
UnityCliLoopTestExecutionRequest parameters = new()
RunTestsSchema parameters = new()
{
TestMode = UnityCliLoopTestMode.PlayMode,
SaveBeforeRun = true
};

UnityCliLoopTestExecutionResult response = await useCase.ExecuteAsync(parameters, CancellationToken.None);
RunTestsResponse response = await useCase.ExecuteAsync(parameters, CancellationToken.None);

Assert.That(response.Success, Is.False);
Assert.That(response.Status, Is.EqualTo(RunTestsExecutionStatus.ExecutionFailed));
Expand Down Expand Up @@ -157,14 +157,14 @@ public async Task ExecuteAsync_WhenNoTestsWereFound_ShouldExposeNoTestsFoundStat
validationService,
NoCleanupWait
);
UnityCliLoopTestExecutionRequest parameters = new()
RunTestsSchema parameters = new()
{
TestMode = UnityCliLoopTestMode.PlayMode,
FilterType = TestFilterType.exact,
FilterValue = "MissingTest"
};

UnityCliLoopTestExecutionResult response = await useCase.ExecuteAsync(parameters, CancellationToken.None);
RunTestsResponse response = await useCase.ExecuteAsync(parameters, CancellationToken.None);

Assert.That(response.Success, Is.False);
Assert.That(response.Status, Is.EqualTo(RunTestsExecutionStatus.NoTestsFound));
Expand Down Expand Up @@ -194,7 +194,7 @@ public async Task ExecuteAsync_AfterTestExecution_ShouldWaitForCleanup()
return Task.CompletedTask;
}
);
UnityCliLoopTestExecutionRequest parameters = new();
RunTestsSchema parameters = new();

await useCase.ExecuteAsync(parameters, CancellationToken.None);

Expand All @@ -221,7 +221,7 @@ public async Task ExecuteAsync_WhenValidationFails_ShouldNotWaitForCleanup()
return Task.CompletedTask;
}
);
UnityCliLoopTestExecutionRequest parameters = new();
RunTestsSchema parameters = new();

await useCase.ExecuteAsync(parameters, CancellationToken.None);

Expand Down Expand Up @@ -250,7 +250,7 @@ public async Task ExecuteAsync_WhenTestFrameworkUnavailable_ShouldNotWaitForClea
return Task.CompletedTask;
}
);
UnityCliLoopTestExecutionRequest parameters = new();
RunTestsSchema parameters = new();

await useCase.ExecuteAsync(parameters, CancellationToken.None);

Expand Down
41 changes: 1 addition & 40 deletions Packages/src/Editor/FirstPartyTools/RunTests/RunTestsTool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,46 +16,7 @@ public class RunTestsTool : UnityCliLoopTool<RunTestsSchema, RunTestsResponse>
protected override async Task<RunTestsResponse> ExecuteAsync(RunTestsSchema parameters, CancellationToken ct)
{
RunTestsUseCase useCase = new();
UnityCliLoopTestExecutionResult result = await useCase.RunTestsAsync(ToRequest(parameters), ct);
return ToResponse(result);
}

private static UnityCliLoopTestExecutionRequest ToRequest(RunTestsSchema parameters)
{
if (parameters == null)
{
throw new System.ArgumentNullException(nameof(parameters));
}

return new UnityCliLoopTestExecutionRequest
{
TestMode = parameters.TestMode,
FilterType = parameters.FilterType,
FilterValue = parameters.FilterValue,
SaveBeforeRun = parameters.SaveBeforeRun,
};
}

private static RunTestsResponse ToResponse(UnityCliLoopTestExecutionResult result)
{
if (result == null)
{
throw new System.ArgumentNullException(nameof(result));
}

return new RunTestsResponse(
success: result.Success,
message: result.Message,
completedAt: result.CompletedAt,
testCount: result.TestCount,
passedCount: result.PassedCount,
failedCount: result.FailedCount,
skippedCount: result.SkippedCount,
xmlPath: result.XmlPath,
status: result.Status,
hasFailures: result.HasFailures,
noTestsFound: result.NoTestsFound,
noTestsFoundExplanation: result.NoTestsFoundExplanation);
return await useCase.ExecuteAsync(parameters, ct);
}
}
}
100 changes: 48 additions & 52 deletions Packages/src/Editor/FirstPartyTools/RunTests/RunTestsUseCase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
/// Processing sequence: 1. Test filter creation, 2. Test execution, 3. Result processing
/// Related classes: RunTestsTool, TestFilterCreationService, TestExecutionService
/// </summary>
public class RunTestsUseCase : IUnityCliLoopTestExecutionService
public class RunTestsUseCase
{
private readonly TestFilterCreationService _filterService;
private readonly TestExecutionService _executionService;
Expand Down Expand Up @@ -51,8 +51,13 @@ public RunTestsUseCase(
/// <param name="parameters">Test execution parameters</param>
/// <param name="ct">Cancellation control token</param>
/// <returns>Test execution result</returns>
public async Task<UnityCliLoopTestExecutionResult> ExecuteAsync(UnityCliLoopTestExecutionRequest parameters, CancellationToken ct)
public async Task<RunTestsResponse> ExecuteAsync(RunTestsSchema parameters, CancellationToken ct)
{
if (parameters == null)
{
throw new System.ArgumentNullException(nameof(parameters));
}

if (!IsSupportedTestMode(parameters.TestMode))
{
return CreateFailureResponse("Unsupported test mode: " + parameters.TestMode);
Expand All @@ -61,7 +66,7 @@ public async Task<UnityCliLoopTestExecutionResult> ExecuteAsync(UnityCliLoopTest
ct.ThrowIfCancellationRequested();
if (!_executionService.IsTestFrameworkAvailable)
{
return CreateTestFrameworkUnavailableResponse();
return RunTestsResponse.CreateTestFrameworkUnavailable();
}

ValidationResult validation = _validationService.Validate(parameters.TestMode, parameters.SaveBeforeRun);
Expand All @@ -76,11 +81,11 @@ public async Task<UnityCliLoopTestExecutionResult> ExecuteAsync(UnityCliLoopTest
{
filter = _filterService.CreateFilter(parameters.FilterType, parameters.FilterValue);
}

// 2. Test execution
ct.ThrowIfCancellationRequested();
SerializableTestResult result;

try
{
if (parameters.TestMode == UnityCliLoopTestMode.PlayMode)
Expand All @@ -102,33 +107,34 @@ public async Task<UnityCliLoopTestExecutionResult> ExecuteAsync(UnityCliLoopTest
// Log full exception details for debugging
UnityEngine.Debug.LogError($"Test execution failed: {ex}");
VibeLogger.LogError(
"test_execution_failed",
"Test execution encountered an error",
"test_execution_failed",
"Test execution encountered an error",
new { testMode = parameters.TestMode, filterType = parameters.FilterType, filterValue = parameters.FilterValue, error = ex.Message }
);
// Create a minimal error result

// Surface the failure; the tool layer converts it into an error response.
throw new System.InvalidOperationException("Test execution failed. Please check the logs for details.", ex);
}

await _waitForTestRunnerCleanupAsync(ct);

// 3. Response creation
UnityCliLoopTestExecutionResult response = new UnityCliLoopTestExecutionResult
{
Success = result.success,
Status = result.status,
HasFailures = result.hasFailures,
NoTestsFound = result.noTestsFound,
NoTestsFoundExplanation = result.noTestsFoundExplanation,
Message = result.message,
CompletedAt = result.completedAt,
TestCount = result.testCount,
PassedCount = result.passedCount,
FailedCount = result.failedCount,
SkippedCount = result.skippedCount,
XmlPath = result.xmlPath
};

// 3. Response creation.
// Why: pass the derived fields explicitly so the constructor does not re-derive them;
// the null-triggered derivation exists only as a source-compat fallback for callers
// that omit the optional arguments.
RunTestsResponse response = new(
success: result.success,
message: result.message,
completedAt: result.completedAt,
testCount: result.testCount,
passedCount: result.passedCount,
failedCount: result.failedCount,
skippedCount: result.skippedCount,
xmlPath: result.xmlPath,
status: result.status,
hasFailures: result.hasFailures,
noTestsFound: result.noTestsFound,
noTestsFoundExplanation: result.noTestsFoundExplanation);
response.Message = RunTestsNoTestsDiagnosticService.AppendDiagnosticsOrOriginalMessage(
response.Message,
() => _noTestsDiagnosticService.AppendDiagnosticsIfNeeded(
Expand All @@ -140,38 +146,28 @@ public async Task<UnityCliLoopTestExecutionResult> ExecuteAsync(UnityCliLoopTest
return response;
}

public Task<UnityCliLoopTestExecutionResult> RunTestsAsync(UnityCliLoopTestExecutionRequest request, CancellationToken ct)
{
return ExecuteAsync(request, ct);
}

private static bool IsSupportedTestMode(UnityCliLoopTestMode testMode)
{
return Enum.IsDefined(typeof(UnityCliLoopTestMode), testMode);
}

private static UnityCliLoopTestExecutionResult CreateTestFrameworkUnavailableResponse()
private static RunTestsResponse CreateFailureResponse(string message)
{
return CreateFailureResponse(RunTestsResponse.TestFrameworkUnavailableMessage);
}

private static UnityCliLoopTestExecutionResult CreateFailureResponse(string message)
{
return new UnityCliLoopTestExecutionResult
{
Success = false,
Status = RunTestsExecutionStatus.ExecutionFailed,
HasFailures = false,
NoTestsFound = false,
NoTestsFoundExplanation = string.Empty,
Message = message,
CompletedAt = DateTime.UtcNow.ToString("o"),
TestCount = 0,
PassedCount = 0,
FailedCount = 0,
SkippedCount = 0,
XmlPath = null
};
// Why: supply every optional argument so the constructor does not re-derive
// status or explanation fields from the failure message.
return new RunTestsResponse(
success: false,
message: message,
completedAt: DateTime.UtcNow.ToString("o"),
testCount: 0,
passedCount: 0,
failedCount: 0,
skippedCount: 0,
xmlPath: null,
status: RunTestsExecutionStatus.ExecutionFailed,
hasFailures: false,
noTestsFound: false,
noTestsFoundExplanation: string.Empty);
}

private static async Task WaitForTestRunnerCleanupAsync(CancellationToken ct)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
using System.Threading;
using System.Threading.Tasks;

namespace io.github.hatayama.UnityCliLoop.FirstPartyTools
{
/// <summary>
/// Defines the Unity CLI Loop Test Execution operations required by the owning workflow.
/// Test mode selector shared between the CLI schema and the run-tests pipeline.
/// </summary>
public interface IUnityCliLoopTestExecutionService
{
Task<UnityCliLoopTestExecutionResult> RunTestsAsync(UnityCliLoopTestExecutionRequest request, CancellationToken ct);
}

public enum UnityCliLoopTestMode
{
EditMode = 0,
PlayMode = 1
}

/// <summary>
/// Filter strategy that determines how the run-tests filter value is interpreted.
/// </summary>
public enum TestFilterType
{
all = 0,
Expand All @@ -35,34 +30,4 @@ internal static class RunTestsExecutionStatus
public const string NoTestsFound = "NoTestsFound";
public const string ExecutionFailed = "ExecutionFailed";
}

/// <summary>
/// Carries the request data needed for Unity CLI Loop Test Execution behavior.
/// </summary>
public sealed class UnityCliLoopTestExecutionRequest
{
public UnityCliLoopTestMode TestMode { get; set; } = UnityCliLoopTestMode.EditMode;
public TestFilterType FilterType { get; set; } = TestFilterType.all;
public string FilterValue { get; set; } = "";
public bool SaveBeforeRun { get; set; } = true;
}

/// <summary>
/// Carries the result data produced by Unity CLI Loop Test Execution behavior.
/// </summary>
public sealed class UnityCliLoopTestExecutionResult
{
public bool Success { get; set; }
public string Status { get; set; } = "";
public bool HasFailures { get; set; }
public bool NoTestsFound { get; set; }
public string NoTestsFoundExplanation { get; set; } = "";
public string Message { get; set; } = "";
public string CompletedAt { get; set; } = "";
public int TestCount { get; set; }
public int PassedCount { get; set; }
public int FailedCount { get; set; }
public int SkippedCount { get; set; }
public string XmlPath { get; set; }
}
}
Loading