Files
ironjava/IronJava.Tests/AstQueryAndTransformationTests.cs
2025-07-21 22:29:22 -04:00

407 lines
14 KiB
C#

using System;
using System.Linq;
using IronJava.Core.AST;
using IronJava.Core.AST.Comparison;
using IronJava.Core.AST.Nodes;
using IronJava.Core.AST.Query;
using IronJava.Core.AST.Transformation;
using IronJava.Core.Serialization;
using Xunit;
namespace IronJava.Tests
{
public class Phase3Tests
{
private static SourceRange CreateLocation() => new(
new SourceLocation(1, 1, 0, 0),
new SourceLocation(1, 1, 0, 0)
);
[Fact]
public void JsonSerializationWorks()
{
// Arrange
var location = CreateLocation();
var method = new MethodDeclaration(
location,
"testMethod",
Modifiers.Public | Modifiers.Static,
new List<Annotation>(),
new PrimitiveType(location, PrimitiveTypeKind.Void),
new List<TypeParameter>(),
new List<Parameter>
{
new Parameter(
location,
new PrimitiveType(location, PrimitiveTypeKind.Int),
"param1",
false,
false,
new List<Annotation>()
)
},
new List<TypeReference>(),
new BlockStatement(location, new List<Statement>()),
null
);
var serializer = new AstJsonSerializer(indented: false);
// Act
var json = serializer.Serialize(method);
// Assert
Assert.Contains("\"nodeType\":\"MethodDeclaration\"", json);
Assert.Contains("\"name\":\"testMethod\"", json);
Assert.Contains("\"modifiers\":[\"public\",\"static\"]", json);
Assert.Contains("\"parameters\"", json);
Assert.Contains("\"param1\"", json);
}
[Fact]
public void AstQueryFindAllWorks()
{
// Arrange
var location = CreateLocation();
var compilation = CreateSampleCompilationUnit();
// Act
var methods = compilation.FindAll<MethodDeclaration>().ToList();
var fields = compilation.FindAll<FieldDeclaration>().ToList();
var classes = compilation.FindAll<ClassDeclaration>().ToList();
// Assert
Assert.Equal(2, methods.Count);
Assert.Single(fields);
Assert.Single(classes);
}
[Fact]
public void AstQueryWhereWorks()
{
// Arrange
var compilation = CreateSampleCompilationUnit();
// Act
var publicMethods = compilation
.Where<MethodDeclaration>(m => m.Modifiers.IsPublic())
.ToList();
var staticFields = compilation
.Where<FieldDeclaration>(f => f.Modifiers.IsStatic())
.ToList();
// Assert
Assert.Single(publicMethods);
Assert.Equal("main", publicMethods[0].Name);
Assert.Empty(staticFields);
}
[Fact]
public void AstQueryBuilderWorks()
{
// Arrange
var compilation = CreateSampleCompilationUnit();
// Act
var result = compilation
.Query<MethodDeclaration>()
.WithName("main")
.WithModifier(Modifiers.Public)
.WithModifier(Modifiers.Static)
.ExecuteFirst();
// Assert
Assert.NotNull(result);
Assert.Equal("main", result.Name);
}
[Fact]
public void AstPatternMatchingWorks()
{
// Arrange
var compilation = CreateSampleCompilationUnit();
var methods = compilation.FindAll<MethodDeclaration>().ToList();
// Act & Assert
Assert.True(methods[0].IsMainMethod()); // main method
Assert.False(methods[1].IsGetter()); // helper method
}
[Fact]
public void AstTransformationRenameWorks()
{
// Arrange
var location = CreateLocation();
var original = new IdentifierExpression(location, "oldName");
var transformer = new IdentifierRenamer("oldName", "newName");
// Act
var transformed = original.Accept(transformer) as IdentifierExpression;
// Assert
Assert.NotNull(transformed);
Assert.Equal("newName", transformed.Name);
}
[Fact]
public void AstTransformationModifierWorks()
{
// Arrange
var compilation = CreateSampleCompilationUnit();
var transformer = ModifierTransformer.AddModifier(Modifiers.Final);
// Act
var transformed = compilation.Accept(transformer) as CompilationUnit;
var transformedClass = transformed?.Types[0] as ClassDeclaration;
// Assert
Assert.NotNull(transformedClass);
Assert.True(transformedClass.Modifiers.IsFinal());
}
[Fact]
public void AstEqualityComparerWorks()
{
// Arrange
var location1 = CreateLocation();
var location2 = new SourceRange(
new SourceLocation(2, 2, 10, 0),
new SourceLocation(2, 2, 10, 0)
);
var expr1 = new IdentifierExpression(location1, "test");
var expr2 = new IdentifierExpression(location1, "test");
var expr3 = new IdentifierExpression(location2, "test");
var expr4 = new IdentifierExpression(location1, "different");
var comparer = new AstEqualityComparer(ignoreLocation: true);
// Act & Assert
Assert.True(comparer.Equals(expr1, expr2));
Assert.True(comparer.Equals(expr1, expr3)); // Different location, but ignored
Assert.False(comparer.Equals(expr1, expr4)); // Different name
}
[Fact]
public void AstDifferWorks()
{
// Arrange
var location = CreateLocation();
var original = new BlockStatement(location, new List<Statement>
{
new ExpressionStatement(location, new IdentifierExpression(location, "a")),
new ExpressionStatement(location, new IdentifierExpression(location, "b"))
});
var modified = new BlockStatement(location, new List<Statement>
{
new ExpressionStatement(location, new IdentifierExpression(location, "a")),
new ExpressionStatement(location, new IdentifierExpression(location, "c")), // Changed
new ExpressionStatement(location, new IdentifierExpression(location, "d")) // Added
});
var differ = new AstDiffer();
// Act
var diff = differ.ComputeDiff(original, modified);
// Assert
Assert.False(diff.IsEmpty);
// The differ detects:
// 1. Modification of the second ExpressionStatement (b -> c)
// 2. Modification of the IdentifierExpression inside it (b -> c)
// 3. Addition of the third ExpressionStatement (d)
// 4. Addition of the IdentifierExpression inside it (d)
// 5. Addition of the fourth statement's expression
// Total = 5 changes when comparing deeply
Assert.Equal(5, diff.TotalChanges);
}
[Fact]
public void TransformationBuilderWorks()
{
// Arrange
var location = CreateLocation();
var classDecl = new ClassDeclaration(
location,
"TestClass",
Modifiers.Public,
new List<Annotation>(),
new List<TypeParameter>(),
null,
new List<TypeReference>(),
new List<MemberDeclaration>
{
new FieldDeclaration(
location,
Modifiers.Private,
new List<Annotation>(),
new PrimitiveType(location, PrimitiveTypeKind.Int),
new List<VariableDeclarator>
{
new VariableDeclarator(location, "oldField", 0, null)
},
null
)
},
null
);
var builder = new TransformationBuilder()
.AddModifier(Modifiers.Final)
.RenameIdentifier("oldField", "newField");
// Act
var transformed = builder.Transform(classDecl) as ClassDeclaration;
// Assert
Assert.NotNull(transformed);
Assert.True(transformed.Modifiers.IsFinal());
var field = transformed.Members[0] as FieldDeclaration;
Assert.NotNull(field);
Assert.Equal("newField", field.Variables[0].Name);
}
[Fact]
public void ComplexQueryScenario()
{
// Arrange
var compilation = CreateComplexCompilationUnit();
// Act
// Find all public static methods in classes that implement Serializable
var query = compilation
.QueryClasses()
.Where(c => c.Interfaces.Any(i => (i as ClassOrInterfaceType)?.Name == "Serializable"))
.Execute()
.SelectMany(c => c.Members.OfType<MethodDeclaration>())
.Where(m => m.Modifiers.IsPublic() && m.Modifiers.IsStatic())
.ToList();
// Assert
Assert.Single(query);
Assert.Equal("serialize", query[0].Name);
}
private CompilationUnit CreateSampleCompilationUnit()
{
var location = CreateLocation();
var mainMethod = new MethodDeclaration(
location,
"main",
Modifiers.Public | Modifiers.Static,
new List<Annotation>(),
new PrimitiveType(location, PrimitiveTypeKind.Void),
new List<TypeParameter>(),
new List<Parameter>
{
new Parameter(
location,
new ArrayType(location,
new ClassOrInterfaceType(location, "String", null, new List<TypeArgument>(), new List<Annotation>()),
1),
"args",
false,
false,
new List<Annotation>()
)
},
new List<TypeReference>(),
new BlockStatement(location, new List<Statement>()),
null
);
var helperMethod = new MethodDeclaration(
location,
"helper",
Modifiers.Private,
new List<Annotation>(),
new PrimitiveType(location, PrimitiveTypeKind.Void),
new List<TypeParameter>(),
new List<Parameter>(),
new List<TypeReference>(),
new BlockStatement(location, new List<Statement>()),
null
);
var field = new FieldDeclaration(
location,
Modifiers.Private,
new List<Annotation>(),
new PrimitiveType(location, PrimitiveTypeKind.Int),
new List<VariableDeclarator>
{
new VariableDeclarator(location, "count", 0, null)
},
null
);
var testClass = new ClassDeclaration(
location,
"TestClass",
Modifiers.Public,
new List<Annotation>(),
new List<TypeParameter>(),
null,
new List<TypeReference>(),
new List<MemberDeclaration> { mainMethod, helperMethod, field },
null
);
return new CompilationUnit(
location,
null,
new List<ImportDeclaration>(),
new List<TypeDeclaration> { testClass }
);
}
private CompilationUnit CreateComplexCompilationUnit()
{
var location = CreateLocation();
var serializableInterface = new ClassOrInterfaceType(
location,
"Serializable",
null,
new List<TypeArgument>(),
new List<Annotation>()
);
var serializeMethod = new MethodDeclaration(
location,
"serialize",
Modifiers.Public | Modifiers.Static,
new List<Annotation>(),
new PrimitiveType(location, PrimitiveTypeKind.Void),
new List<TypeParameter>(),
new List<Parameter>(),
new List<TypeReference>(),
null,
null
);
var dataClass = new ClassDeclaration(
location,
"DataClass",
Modifiers.Public,
new List<Annotation>(),
new List<TypeParameter>(),
null,
new List<TypeReference> { serializableInterface },
new List<MemberDeclaration> { serializeMethod },
null
);
return new CompilationUnit(
location,
null,
new List<ImportDeclaration>(),
new List<TypeDeclaration> { dataClass }
);
}
}
}