Files
irongo/tests/IronGo.Tests/IntegrationTests/RealGoCodeTests.cs
2025-07-23 00:47:04 -04:00

285 lines
12 KiB
C#

using FluentAssertions;
using MarketAlly.IronGo;
using MarketAlly.IronGo.AST;
using MarketAlly.IronGo.Serialization;
using MarketAlly.IronGo.Utilities;
using System.IO;
using System.Linq;
using Xunit;
namespace MarketAlly.IronGo.Tests.IntegrationTests;
public class RealGoCodeTests
{
private string GetTestDataPath(string filename)
{
return Path.Combine("TestData", filename);
}
[Fact]
public void Parse_HelloWorldGo_ShouldParseCorrectly()
{
// Arrange
var path = GetTestDataPath("hello.go");
var source = File.ReadAllText(path);
// Act
var ast = IronGoParser.Parse(source);
// Assert
ast.Should().NotBeNull();
ast.Package!.Name.Should().Be("main");
ast.Imports.Should().HaveCount(1);
ast.GetImportedPackages().Should().ContainSingle("fmt");
var mainFunc = ast.FindFunction("main");
mainFunc.Should().NotBeNull();
mainFunc!.Parameters.Should().BeEmpty();
mainFunc.Body!.Statements.Should().HaveCount(1);
var calls = ast.GetAllCalls().ToList();
calls.Should().HaveCount(1);
var printlnCall = calls[0];
printlnCall.Function.Should().BeOfType<SelectorExpression>()
.Which.Selector.Should().Be("Println");
}
[Fact]
public void Parse_TypesGo_ShouldParseAllTypeDeclarations()
{
// Arrange
var path = GetTestDataPath("types.go");
var source = File.ReadAllText(path);
// Act
var result = IronGoParser.ParseWithDiagnostics(source);
// Assert
result.Diagnostics.Errors.Should().BeEmpty();
var ast = result.SourceFile;
ast.Package!.Name.Should().Be("types");
// Check type declarations
var types = ast.GetTypes().ToList();
types.Should().Contain(t => t.Name == "ID");
types.Should().Contain(t => t.Name == "Person");
types.Should().Contain(t => t.Name == "Address");
types.Should().Contain(t => t.Name == "Writer");
types.Should().Contain(t => t.Name == "Reader");
types.Should().Contain(t => t.Name == "ReadWriter");
types.Should().Contain(t => t.Name == "List");
types.Should().Contain(t => t.Name == "Map");
// Check struct fields
var personType = types.First(t => t.Name == "Person");
var personStruct = personType.Specs[0].Type.Should().BeOfType<StructType>().Subject;
personStruct.Fields.Should().HaveCountGreaterThan(5);
personStruct.Fields[0].Tag.Should().Contain("json:");
// Check interfaces
var writerType = types.First(t => t.Name == "Writer");
var writerInterface = writerType.Specs[0].Type.Should().BeOfType<InterfaceType>().Subject;
writerInterface.Methods.Should().HaveCount(1);
// Check generic types
var listType = types.First(t => t.Name == "List");
listType.Specs[0].TypeParameters.Should().HaveCount(1);
listType.Specs[0].TypeParameters![0].Name.Should().Be("T");
// Check methods
var methods = ast.GetMethods().ToList();
methods.Should().Contain(m => m.Name == "FullName");
methods.Should().Contain(m => m.Name == "IsAdult");
methods.Should().Contain(m => m.Name == "Add");
methods.Should().Contain(m => m.Name == "Get");
}
[Fact]
public void Parse_ComplexGo_ShouldHandleComplexLanguageFeatures()
{
// Arrange
var path = GetTestDataPath("complex.go");
var source = File.ReadAllText(path);
// Act
var result = IronGoParser.ParseWithDiagnostics(source);
// Assert
result.Diagnostics.Errors.Should().BeEmpty();
result.Diagnostics.TokenCount.Should().BeGreaterThan(1000);
var ast = result.SourceFile;
// Check imports
var imports = ast.GetImportedPackages().ToList();
imports.Should().Contain("context");
imports.Should().Contain("errors");
imports.Should().Contain("fmt");
imports.Should().Contain("log");
imports.Should().Contain("sync");
imports.Should().Contain("time");
// Check constants
var constDeclarations = ast.FindNodes<ConstDeclaration>().ToList();
constDeclarations.Should().HaveCount(2); // Two const blocks
var allConstants = constDeclarations.SelectMany(c => c.Specs).ToList();
allConstants.Should().HaveCountGreaterThan(3); // More than 3 individual constants
// Check global variables
var globals = ast.Declarations.OfType<VariableDeclaration>().ToList();
globals.Should().HaveCount(1); // One var block with multiple specs
// Check complex struct types
var serverType = ast.GetTypes().First(t => t.Name == "Server");
var serverStruct = serverType.Specs[0].Type as StructType;
serverStruct.Should().NotBeNull();
serverStruct!.Fields.Should().HaveCountGreaterThan(5);
// Check methods with goroutines
var startMethod = ast.GetMethods().First(m => m.Name == "Start");
var goStatements = startMethod.Body!.FindNodes<GoStatement>().ToList();
goStatements.Should().HaveCountGreaterThan(1);
// Check channels
var channelTypes = ast.FindNodes<ChannelType>().ToList();
channelTypes.Should().NotBeEmpty();
// Check select statements
var selectStatements = ast.FindNodes<SelectStatement>().ToList();
selectStatements.Should().NotBeEmpty();
// Check defer statements
var deferStatements = ast.FindNodes<DeferStatement>().ToList();
deferStatements.Should().HaveCountGreaterThan(3);
// Check generic functions
var genericFunctions = ast.GetFunctions()
.Where(f => f.TypeParameters != null && f.TypeParameters.Count > 0)
.ToList();
genericFunctions.Should().NotBeEmpty();
genericFunctions.Should().Contain(f => f.Name == "Map");
genericFunctions.Should().Contain(f => f.Name == "Sum");
// Check error handling
var errorReturns = ast.GetFunctions()
.Where(f => f.ReturnParameters?.Any(p =>
p.Type is IdentifierType ident && ident.Name == "error") == true)
.ToList();
errorReturns.Should().NotBeEmpty();
// Check init function
var initFunc = ast.FindFunction("init");
initFunc.Should().NotBeNull();
}
[Fact]
public void JsonSerialization_ComplexGo_ShouldSerializeWithoutErrors()
{
// Arrange
var path = GetTestDataPath("complex.go");
var source = File.ReadAllText(path);
var ast = IronGoParser.Parse(source);
// Act
var json = ast.ToJsonCompact();
// Assert
json.Should().NotBeNullOrWhiteSpace();
json.Length.Should().BeGreaterThan(10000); // Complex AST should produce substantial JSON
json.Should().Contain("\"Type\":\"SourceFile\""); // Compact JSON has no spaces
json.Should().Contain("\"Package\":");
json.Should().Contain("Server");
json.Should().Contain("Client");
// The word "goroutine" appears in comments, but JSON doesn't serialize comments
}
[Fact]
public void Performance_ParseComplexFile_ShouldBeFast()
{
// Arrange
var path = GetTestDataPath("complex.go");
var source = File.ReadAllText(path);
var parser = new IronGoParser();
// Warm up cache
parser.ParseSource(source);
// Act
var result = parser.ParseSourceWithDiagnostics(source);
// Assert
result.Diagnostics.ParseTimeMs.Should().BeLessThan(5); // Should be very fast from cache
}
[Fact]
public void Visitor_ComplexGo_ShouldVisitAllNodeTypes()
{
// Arrange
var path = GetTestDataPath("complex.go");
var source = File.ReadAllText(path);
var ast = IronGoParser.Parse(source);
// Act
var visitor = new NodeTypeCollector();
ast.Accept(visitor);
// Assert
visitor.NodeTypes.Should().Contain(typeof(SourceFile));
visitor.NodeTypes.Should().Contain(typeof(PackageDeclaration));
visitor.NodeTypes.Should().Contain(typeof(ImportDeclaration));
visitor.NodeTypes.Should().Contain(typeof(FunctionDeclaration));
visitor.NodeTypes.Should().Contain(typeof(MethodDeclaration));
visitor.NodeTypes.Should().Contain(typeof(TypeDeclaration));
visitor.NodeTypes.Should().Contain(typeof(VariableDeclaration));
visitor.NodeTypes.Should().Contain(typeof(ConstDeclaration));
visitor.NodeTypes.Should().Contain(typeof(StructType));
visitor.NodeTypes.Should().Contain(typeof(InterfaceType));
visitor.NodeTypes.Should().Contain(typeof(ChannelType));
visitor.NodeTypes.Should().Contain(typeof(MapType));
visitor.NodeTypes.Should().Contain(typeof(SliceType));
visitor.NodeTypes.Should().Contain(typeof(GoStatement));
visitor.NodeTypes.Should().Contain(typeof(DeferStatement));
visitor.NodeTypes.Should().Contain(typeof(SelectStatement));
visitor.NodeTypes.Should().Contain(typeof(ForStatement));
visitor.NodeTypes.Should().Contain(typeof(IfStatement));
visitor.NodeTypes.Should().Contain(typeof(SwitchStatement));
visitor.NodeTypes.Should().Contain(typeof(CallExpression));
visitor.NodeTypes.Should().Contain(typeof(BinaryExpression));
visitor.NodeTypes.Should().Contain(typeof(UnaryExpression));
}
}
public class NodeTypeCollector : GoAstWalker
{
public HashSet<Type> NodeTypes { get; } = new();
private void CollectType(IGoNode node)
{
NodeTypes.Add(node.GetType());
}
public override void VisitSourceFile(SourceFile node) { CollectType(node); base.VisitSourceFile(node); }
public override void VisitPackageDeclaration(PackageDeclaration node) { CollectType(node); base.VisitPackageDeclaration(node); }
public override void VisitImportDeclaration(ImportDeclaration node) { CollectType(node); base.VisitImportDeclaration(node); }
public override void VisitFunctionDeclaration(FunctionDeclaration node) { CollectType(node); base.VisitFunctionDeclaration(node); }
public override void VisitMethodDeclaration(MethodDeclaration node) { CollectType(node); base.VisitMethodDeclaration(node); }
public override void VisitTypeDeclaration(TypeDeclaration node) { CollectType(node); base.VisitTypeDeclaration(node); }
public override void VisitConstDeclaration(ConstDeclaration node) { CollectType(node); base.VisitConstDeclaration(node); }
public override void VisitVariableDeclaration(VariableDeclaration node) { CollectType(node); base.VisitVariableDeclaration(node); }
public override void VisitStructType(StructType node) { CollectType(node); base.VisitStructType(node); }
public override void VisitInterfaceType(InterfaceType node) { CollectType(node); base.VisitInterfaceType(node); }
public override void VisitChannelType(ChannelType node) { CollectType(node); base.VisitChannelType(node); }
public override void VisitMapType(MapType node) { CollectType(node); base.VisitMapType(node); }
public override void VisitSliceType(SliceType node) { CollectType(node); base.VisitSliceType(node); }
public override void VisitGoStatement(GoStatement node) { CollectType(node); base.VisitGoStatement(node); }
public override void VisitDeferStatement(DeferStatement node) { CollectType(node); base.VisitDeferStatement(node); }
public override void VisitSelectStatement(SelectStatement node) { CollectType(node); base.VisitSelectStatement(node); }
public override void VisitForStatement(ForStatement node) { CollectType(node); base.VisitForStatement(node); }
public override void VisitIfStatement(IfStatement node) { CollectType(node); base.VisitIfStatement(node); }
public override void VisitSwitchStatement(SwitchStatement node) { CollectType(node); base.VisitSwitchStatement(node); }
public override void VisitCallExpression(CallExpression node) { CollectType(node); base.VisitCallExpression(node); }
public override void VisitBinaryExpression(BinaryExpression node) { CollectType(node); base.VisitBinaryExpression(node); }
public override void VisitUnaryExpression(UnaryExpression node) { CollectType(node); base.VisitUnaryExpression(node); }
}