using System.Diagnostics; using System.Text; using TinyCC.Core; namespace TinyCC.E2ETests; /// /// 端到端测试运行器 /// 负责编译 C 源代码并执行生成的 ELF 文件 /// public sealed class E2ETestRunner { private readonly string _tempDirectory; public E2ETestRunner(string? tempDirectory = null) { _tempDirectory = tempDirectory ?? "/tmp/tinycc-debug"; // 固定路径便于调试 Directory.CreateDirectory(_tempDirectory); } /// /// 运行单个测试用例 /// public async Task 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 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() { // 调试期间保留文件 } } /// /// 测试用例 /// public record TestCase( string Name, string SourceCode, int ExpectedExitCode, string? ExpectedOutput = null ); /// /// 测试结果 /// public record TestResult( string TestCaseName, bool Passed, int ActualExitCode, string? ActualOutput, string? ErrorMessage ); /// /// 编译结果 /// public record CompilationResult( bool Success, string? Error ); /// /// 执行结果 /// public record ExecutionResult( int ExitCode, string Output );