Refactor code structure for improved readability and maintainability; optimize performance in key functions.
This commit is contained in:
124
tests/interop/StellaOps.Interop.Tests/ToolManager.cs
Normal file
124
tests/interop/StellaOps.Interop.Tests/ToolManager.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
namespace StellaOps.Interop.Tests;
|
||||
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Manages execution of external tools for interop testing.
|
||||
/// </summary>
|
||||
public sealed class ToolManager
|
||||
{
|
||||
private readonly string _workDir;
|
||||
|
||||
public ToolManager(string workDir)
|
||||
{
|
||||
_workDir = workDir;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify that a tool is available and executable.
|
||||
/// </summary>
|
||||
public async Task<bool> VerifyToolAsync(string toolName, string testArgs, CancellationToken ct = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await RunAsync(toolName, testArgs, ct);
|
||||
return result.Success || result.ExitCode == 0; // Some tools return 0 even on --version
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run an external tool with arguments.
|
||||
/// </summary>
|
||||
public async Task<ToolResult> RunAsync(
|
||||
string toolName,
|
||||
string arguments,
|
||||
CancellationToken ct = default,
|
||||
int timeoutMs = 300000) // 5 minute default timeout
|
||||
{
|
||||
var startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = toolName,
|
||||
Arguments = arguments,
|
||||
WorkingDirectory = _workDir,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
|
||||
using var process = new Process { StartInfo = startInfo };
|
||||
var outputBuilder = new StringBuilder();
|
||||
var errorBuilder = new StringBuilder();
|
||||
|
||||
process.OutputDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data != null)
|
||||
outputBuilder.AppendLine(e.Data);
|
||||
};
|
||||
|
||||
process.ErrorDataReceived += (sender, e) =>
|
||||
{
|
||||
if (e.Data != null)
|
||||
errorBuilder.AppendLine(e.Data);
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
|
||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(ct);
|
||||
cts.CancelAfter(timeoutMs);
|
||||
|
||||
await process.WaitForExitAsync(cts.Token);
|
||||
|
||||
var output = outputBuilder.ToString();
|
||||
var error = errorBuilder.ToString();
|
||||
var exitCode = process.ExitCode;
|
||||
|
||||
return new ToolResult(
|
||||
Success: exitCode == 0,
|
||||
ExitCode: exitCode,
|
||||
Output: output,
|
||||
Error: string.IsNullOrWhiteSpace(error) ? null : error);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!process.HasExited)
|
||||
process.Kill();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore kill failures
|
||||
}
|
||||
|
||||
return new ToolResult(
|
||||
Success: false,
|
||||
ExitCode: -1,
|
||||
Output: outputBuilder.ToString(),
|
||||
Error: $"Tool execution timed out after {timeoutMs}ms");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ToolResult(
|
||||
Success: false,
|
||||
ExitCode: -1,
|
||||
Output: outputBuilder.ToString(),
|
||||
Error: $"Tool execution failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record ToolResult(
|
||||
bool Success,
|
||||
int ExitCode,
|
||||
string Output,
|
||||
string? Error = null);
|
||||
Reference in New Issue
Block a user