commit initial

This commit is contained in:
baishi
2026-05-30 07:59:28 +08:00
commit cbefad339f
39 changed files with 7736 additions and 0 deletions

View File

@@ -0,0 +1,238 @@
using System.Diagnostics;
using System.Text;
using TinyCC.Core;
namespace TinyCC.E2ETests;
/// <summary>
/// 端到端测试运行器
/// 负责编译 C 源代码并执行生成的 ELF 文件
/// </summary>
public sealed class E2ETestRunner
{
private readonly string _tempDirectory;
public E2ETestRunner(string? tempDirectory = null)
{
_tempDirectory = tempDirectory ?? "/tmp/tinycc-debug"; // 固定路径便于调试
Directory.CreateDirectory(_tempDirectory);
}
/// <summary>
/// 运行单个测试用例
/// </summary>
public async Task<TestResult> RunTestAsync(TestCase testCase)
{
var sourceFile = Path.Combine(_tempDirectory, $"{testCase.Name}.c");
var outputFile = Path.Combine(_tempDirectory, $"{testCase.Name}.out");
try
{
// 写入源代码
await File.WriteAllTextAsync(sourceFile, testCase.SourceCode);
// 编译
var compileResult = Compile(sourceFile, outputFile);
if (!compileResult.Success)
{
return new TestResult(
testCase.Name,
false,
-1,
null,
$"编译失败: {compileResult.Error}"
);
}
// 执行
var executeResult = await ExecuteAsync(outputFile);
// 调试:如果失败,打印 objdump
if (executeResult.ExitCode != testCase.ExpectedExitCode)
{
try
{
var psi = new ProcessStartInfo("objdump", $"-d -M intel \"{outputFile}\"")
{
RedirectStandardOutput = true,
UseShellExecute = false
};
using var p = Process.Start(psi)!;
var dump = await p.StandardOutput.ReadToEndAsync();
Console.WriteLine($"[DEBUG] objdump:\n{dump}");
}
catch { }
}
// 验证结果
bool passed = executeResult.ExitCode == testCase.ExpectedExitCode;
if (testCase.ExpectedOutput != null && executeResult.Output != testCase.ExpectedOutput)
{
passed = false;
}
return new TestResult(
testCase.Name,
passed,
executeResult.ExitCode,
executeResult.Output,
passed ? null : $"期望退出码 {testCase.ExpectedExitCode},实际 {executeResult.ExitCode}"
);
}
catch (Exception ex)
{
return new TestResult(
testCase.Name,
false,
-1,
null,
$"测试执行异常: {ex.Message}"
);
}
}
private CompilationResult Compile(string sourceFile, string outputFile)
{
try
{
var errorReporter = new ErrorReporter();
var driver = new CompilerDriver(errorReporter);
var options = new CompilationOptions(
SourceFile: sourceFile,
OutputFile: outputFile,
Platform: TargetPlatform.LinuxX64
);
var result = driver.Compile(options);
if (errorReporter.HasErrors)
{
var errors = string.Join("\n", errorReporter.GetErrors().Select(e => e.ToString()));
return new CompilationResult(false, errors);
}
if (!result.Success)
{
return new CompilationResult(false, result.Message);
}
return new CompilationResult(true, null);
}
catch (Exception ex)
{
return new CompilationResult(false, ex.Message);
}
}
private async Task<ExecutionResult> ExecuteAsync(string executablePath)
{
try
{
// 设置执行权限
var chmod = Process.Start(new ProcessStartInfo
{
FileName = "chmod",
Arguments = $"+x \"{executablePath}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false
});
if (chmod != null)
{
await chmod.WaitForExitAsync();
}
var psi = new ProcessStartInfo
{
FileName = executablePath,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
using var process = Process.Start(psi)
?? throw new InvalidOperationException($"无法启动进程: {executablePath}");
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);
}
};
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync();
var output = outputBuilder.ToString().TrimEnd();
var error = errorBuilder.ToString().TrimEnd();
return new ExecutionResult(
process.ExitCode,
string.IsNullOrEmpty(output) ? error : output
);
}
catch (Exception ex)
{
return new ExecutionResult(-1, $"执行失败: {ex.Message}");
}
}
public void Cleanup()
{
// 调试期间保留文件
}
}
/// <summary>
/// 测试用例
/// </summary>
public record TestCase(
string Name,
string SourceCode,
int ExpectedExitCode,
string? ExpectedOutput = null
);
/// <summary>
/// 测试结果
/// </summary>
public record TestResult(
string TestCaseName,
bool Passed,
int ActualExitCode,
string? ActualOutput,
string? ErrorMessage
);
/// <summary>
/// 编译结果
/// </summary>
public record CompilationResult(
bool Success,
string? Error
);
/// <summary>
/// 执行结果
/// </summary>
public record ExecutionResult(
int ExitCode,
string Output
);