106 lines
3.6 KiB
C#
106 lines
3.6 KiB
C#
namespace LiquidCode.Tester.Worker.Services;
|
|
|
|
public class OutputCheckerService : IOutputCheckerService
|
|
{
|
|
private readonly ILogger<OutputCheckerService> _logger;
|
|
private readonly CheckerService _checkerService;
|
|
private readonly CheckerServiceIsolate _checkerServiceIsolate;
|
|
private readonly bool _useIsolate;
|
|
|
|
public OutputCheckerService(
|
|
ILogger<OutputCheckerService> logger,
|
|
CheckerService checkerService,
|
|
CheckerServiceIsolate checkerServiceIsolate,
|
|
IConfiguration configuration)
|
|
{
|
|
_logger = logger;
|
|
_checkerService = checkerService;
|
|
_checkerServiceIsolate = checkerServiceIsolate;
|
|
_useIsolate = configuration.GetValue<bool>("Isolate:Enabled", false);
|
|
|
|
if (_useIsolate)
|
|
{
|
|
_logger.LogInformation("Using Isolate sandbox for checker execution");
|
|
}
|
|
}
|
|
|
|
public async Task<bool> CheckOutputAsync(string actualOutput, string expectedOutputPath)
|
|
{
|
|
try
|
|
{
|
|
var expectedOutput = await File.ReadAllTextAsync(expectedOutputPath);
|
|
|
|
// Normalize outputs for comparison
|
|
var normalizedActual = NormalizeOutput(actualOutput);
|
|
var normalizedExpected = NormalizeOutput(expectedOutput);
|
|
|
|
var match = normalizedActual == normalizedExpected;
|
|
|
|
if (!match)
|
|
{
|
|
_logger.LogDebug("Output mismatch. Expected length: {ExpectedLength}, Actual length: {ActualLength}",
|
|
normalizedExpected.Length, normalizedActual.Length);
|
|
}
|
|
|
|
return match;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error checking output against {ExpectedFile}", expectedOutputPath);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private string NormalizeOutput(string output)
|
|
{
|
|
// Remove trailing whitespace from each line and normalize line endings
|
|
var lines = output.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None)
|
|
.Select(line => line.TrimEnd())
|
|
.ToList();
|
|
|
|
// Remove trailing empty lines
|
|
while (lines.Count > 0 && string.IsNullOrWhiteSpace(lines[^1]))
|
|
{
|
|
lines.RemoveAt(lines.Count - 1);
|
|
}
|
|
|
|
// Remove leading empty lines
|
|
while (lines.Count > 0 && string.IsNullOrWhiteSpace(lines[0]))
|
|
{
|
|
lines.RemoveAt(0);
|
|
}
|
|
|
|
return string.Join("\n", lines);
|
|
}
|
|
|
|
public async Task<bool> CheckOutputWithCheckerAsync(
|
|
string actualOutput,
|
|
string inputFilePath,
|
|
string expectedOutputPath,
|
|
string? checkerPath)
|
|
{
|
|
// If custom checker is available, use it
|
|
if (!string.IsNullOrEmpty(checkerPath) && File.Exists(checkerPath))
|
|
{
|
|
_logger.LogDebug("Using custom checker: {CheckerPath} (Isolate: {UseIsolate})",
|
|
checkerPath, _useIsolate);
|
|
|
|
var checkerResult = _useIsolate
|
|
? await _checkerServiceIsolate.CheckAsync(checkerPath, inputFilePath, actualOutput, expectedOutputPath)
|
|
: await _checkerService.CheckAsync(checkerPath, inputFilePath, actualOutput, expectedOutputPath);
|
|
|
|
if (!checkerResult.Accepted)
|
|
{
|
|
_logger.LogWarning("Custom checker verdict: {Verdict} - {Message}",
|
|
checkerResult.Verdict, checkerResult.Message);
|
|
}
|
|
|
|
return checkerResult.Accepted;
|
|
}
|
|
|
|
// Fall back to standard string comparison
|
|
_logger.LogDebug("No custom checker, using standard comparison");
|
|
return await CheckOutputAsync(actualOutput, expectedOutputPath);
|
|
}
|
|
}
|