Files
LiquidCode.Tester/src/LiquidCode.Tester.Worker/Services/OutputCheckerService.cs
2025-11-04 23:16:13 +04:00

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);
}
}