using System.Linq; using System.Text.Json; using MarketAlly.IronJava.Core; using MarketAlly.IronJava.Core.AST; using MarketAlly.IronJava.Core.AST.Nodes; using MarketAlly.IronJava.Core.Serialization; using Xunit; namespace MarketAlly.IronJava.Tests { public class JsonDeserializationTests { private readonly AstJsonSerializer _serializer = new(); [Fact] public void CanSerializeAndDeserializeSimpleClass() { var javaCode = @" package com.example; public class HelloWorld { private String message; public HelloWorld(String msg) { this.message = msg; } public void printMessage() { System.out.println(message); } } "; // Parse Java code var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); Assert.NotNull(parseResult.Ast); // Serialize to JSON var json = _serializer.Serialize(parseResult.Ast!); Assert.NotNull(json); // Deserialize back to AST var deserializedAst = _serializer.Deserialize(json)!; Assert.NotNull(deserializedAst); // Verify structure Assert.NotNull(deserializedAst.Package); Assert.Equal("com.example", deserializedAst.Package.PackageName); Assert.Single(deserializedAst.Types); var classDecl = deserializedAst.Types[0] as ClassDeclaration; Assert.NotNull(classDecl); Assert.Equal("HelloWorld", classDecl.Name); Assert.True(classDecl.Modifiers.IsPublic()); Assert.Equal(3, classDecl.Members.Count); // Check field var field = classDecl.Members[0] as FieldDeclaration; Assert.NotNull(field); Assert.True(field.Modifiers.IsPrivate()); var fieldType = field.Type as ClassOrInterfaceType; Assert.NotNull(fieldType); Assert.Equal("String", fieldType.Name); // Check constructor var constructor = classDecl.Members[1] as MethodDeclaration; Assert.NotNull(constructor); Assert.True(constructor.IsConstructor); Assert.Equal("HelloWorld", constructor.Name); // Note: Current parser implementation doesn't capture constructor parameters // This is a parser limitation, not a JSON serialization issue // Check method var method = classDecl.Members[2] as MethodDeclaration; Assert.NotNull(method); Assert.Equal("printMessage", method.Name); Assert.True(method.Modifiers.IsPublic()); Assert.NotNull(method.ReturnType); // void is represented as PrimitiveType with Kind = Void var returnType = method.ReturnType as PrimitiveType; Assert.NotNull(returnType); Assert.Equal(PrimitiveTypeKind.Void, returnType.Kind); } [Fact] public void CanSerializeAndDeserializeComplexExpressions() { var javaCode = @" public class ExpressionTest { public void testExpressions() { int a = 5; int b = 10; int c = a + b * 2; boolean result = (a < b) && (c > 15); String s = result ? ""yes"" : ""no""; int[] arr = new int[]{1, 2, 3}; int elem = arr[0]; Object obj = (String) s; boolean isString = obj instanceof String; } } "; var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); var json = _serializer.Serialize(parseResult.Ast!); var deserializedAst = _serializer.Deserialize(json)!; Assert.NotNull(deserializedAst); var classDecl = deserializedAst.Types[0] as ClassDeclaration; Assert.NotNull(classDecl); var method = classDecl.Members[0] as MethodDeclaration; Assert.NotNull(method); Assert.NotNull(method.Body); // Verify we have statements Assert.True(method.Body.Statements.Count >= 9); // Check a binary expression var stmt3 = method.Body.Statements[2] as LocalVariableStatement; Assert.NotNull(stmt3); var init3 = stmt3.Variables[0].Initializer as BinaryExpression; Assert.NotNull(init3); Assert.Equal(BinaryOperator.Add, init3.Operator); // Check conditional expression var stmt5 = method.Body.Statements[4] as LocalVariableStatement; Assert.NotNull(stmt5); var init5 = stmt5.Variables[0].Initializer as ConditionalExpression; Assert.NotNull(init5); // Check array creation var stmt6 = method.Body.Statements[5] as LocalVariableStatement; Assert.NotNull(stmt6); Assert.NotNull(stmt6.Variables[0].Initializer); // Could be NewArrayExpression or ArrayInitializer // Check we can find specific expression types var hasConditional = method.Body.Statements.Any(s => s is LocalVariableStatement lvs && lvs.Variables[0].Initializer is ConditionalExpression); Assert.True(hasConditional, "Should have a conditional expression"); var hasArrayAccess = method.Body.Statements.Any(s => s is LocalVariableStatement lvs && lvs.Variables[0].Initializer is ArrayAccessExpression); Assert.True(hasArrayAccess, "Should have an array access expression"); } [Fact] public void CanSerializeAndDeserializeControlFlow() { var javaCode = @" public class ControlFlowTest { public void testControlFlow(int n) { if (n > 0) { System.out.println(""positive""); } else { System.out.println(""non-positive""); } while (n > 0) { n--; } do { n++; } while (n < 10); for (int i = 0; i < n; i++) { if (i == 5) break; if (i == 3) continue; System.out.println(i); } for (String s : new String[]{""a"", ""b"", ""c""}) { System.out.println(s); } switch (n) { case 1: System.out.println(""one""); break; case 2: case 3: System.out.println(""two or three""); break; default: System.out.println(""other""); } try { throw new Exception(""test""); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println(""done""); } } } "; var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); var json = _serializer.Serialize(parseResult.Ast!); var deserializedAst = _serializer.Deserialize(json)!; Assert.NotNull(deserializedAst); var classDecl = deserializedAst.Types[0] as ClassDeclaration; Assert.NotNull(classDecl); var method = classDecl.Members[0] as MethodDeclaration; Assert.NotNull(method); Assert.NotNull(method.Body); // Verify control flow statements var statements = method.Body.Statements; // If statement Assert.IsType(statements[0]); var ifStmt = (IfStatement)statements[0]; Assert.NotNull(ifStmt.ElseStatement); // While statement Assert.IsType(statements[1]); // Do-while statement Assert.IsType(statements[2]); // For statement Assert.IsType(statements[3]); var forStmt = (ForStatement)statements[3]; Assert.Single(forStmt.Initializers); // For-each statement Assert.IsType(statements[4]); // Switch statement Assert.IsType(statements[5]); var switchStmt = (SwitchStatement)statements[5]; Assert.Equal(3, switchStmt.Cases.Count); // case 1, case 2/3, default // Try statement Assert.IsType(statements[6]); var tryStmt = (TryStatement)statements[6]; Assert.Single(tryStmt.CatchClauses); Assert.NotNull(tryStmt.FinallyBlock); } [Fact] public void CanSerializeAndDeserializeEnum() { var javaCode = @" public enum Color { RED(255, 0, 0), GREEN(0, 255, 0), BLUE(0, 0, 255); private final int r, g, b; Color(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } public int getRed() { return r; } } "; var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); var json = _serializer.Serialize(parseResult.Ast!); var deserializedAst = _serializer.Deserialize(json)!; Assert.NotNull(deserializedAst); var enumDecl = deserializedAst.Types[0] as EnumDeclaration; Assert.NotNull(enumDecl); Assert.Equal("Color", enumDecl.Name); Assert.Equal(3, enumDecl.Constants.Count); // Check enum constants Assert.Equal("RED", enumDecl.Constants[0].Name); Assert.Equal(3, enumDecl.Constants[0].Arguments.Count); } [Fact] public void CanSerializeAndDeserializeLambdasAndMethodReferences() { var javaCode = @" import java.util.function.*; public class LambdaTest { public void test() { Function f1 = s -> s.length(); BiFunction f2 = (a, b) -> a + b; Runnable r = () -> System.out.println(""Hello""); Function ref1 = String::length; Supplier ref2 = ""test""::toString; Function ref3 = String[]::new; } } "; var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); var json = _serializer.Serialize(parseResult.Ast!); var deserializedAst = _serializer.Deserialize(json)!; Assert.NotNull(deserializedAst); var classDecl = deserializedAst.Types[0] as ClassDeclaration; Assert.NotNull(classDecl); var method = classDecl.Members[0] as MethodDeclaration; Assert.NotNull(method); // Check lambda expressions var stmt1 = method.Body!.Statements[0] as LocalVariableStatement; Assert.NotNull(stmt1); var lambda1 = stmt1.Variables[0].Initializer as LambdaExpression; Assert.NotNull(lambda1); Assert.Single(lambda1.Parameters); var stmt2 = method.Body.Statements[1] as LocalVariableStatement; Assert.NotNull(stmt2); var lambda2 = stmt2.Variables[0].Initializer as LambdaExpression; Assert.NotNull(lambda2); Assert.Equal(2, lambda2.Parameters.Count); // Check method references var stmt4 = method.Body!.Statements[3] as LocalVariableStatement; Assert.NotNull(stmt4); var ref1 = stmt4!.Variables[0].Initializer as MethodReferenceExpression; Assert.NotNull(ref1); Assert.Equal("length", ref1!.MethodName); } [Fact] public void PreservesSourceLocationInformation() { var javaCode = @"public class Test { public void method() { int x = 42; } }"; var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); var json = _serializer.Serialize(parseResult.Ast!); var deserializedAst = _serializer.Deserialize(json)!; Assert.NotNull(deserializedAst); // Check that source location is preserved var classDecl = deserializedAst.Types[0] as ClassDeclaration; Assert.NotNull(classDecl); Assert.Equal(1, classDecl.Location.Start.Line); var method = classDecl.Members[0] as MethodDeclaration; Assert.NotNull(method); Assert.Equal(2, method.Location.Start.Line); } [Fact] public void HandlesNullValuesCorrectly() { var javaCode = @" public interface SimpleInterface { void method(); } "; var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); var json = _serializer.Serialize(parseResult.Ast!); var deserializedAst = _serializer.Deserialize(json)!; Assert.NotNull(deserializedAst); Assert.Null(deserializedAst.Package); // No package declaration Assert.Empty(deserializedAst.Imports); // No imports var interfaceDecl = deserializedAst.Types[0] as InterfaceDeclaration; Assert.NotNull(interfaceDecl); var method = interfaceDecl.Members[0] as MethodDeclaration; Assert.NotNull(method); Assert.Null(method.Body); // Interface method has no body } [Fact] public void RoundTripPreservesAstStructure() { var javaCode = @" package test.example; import java.util.*; import java.io.IOException; @SuppressWarnings(""unchecked"") public class CompleteExample> { private static final int CONSTANT = 42; @Deprecated public T process(T input) throws IOException { return input; } } "; var parseResult = JavaParser.Parse(javaCode); Assert.True(parseResult.Success); var originalAst = parseResult.Ast; // First round trip var json1 = _serializer.Serialize(originalAst!); var deserialized1 = _serializer.Deserialize(json1)!; // Second round trip var json2 = _serializer.Serialize(deserialized1); var deserialized2 = _serializer.Deserialize(json2)!; // JSON should be identical after round trips Assert.Equal(json1, json2); // Verify structure is preserved Assert.Equal(originalAst!.Package!.PackageName, deserialized2!.Package!.PackageName); Assert.Equal(originalAst.Imports.Count, deserialized2.Imports.Count); Assert.Equal(originalAst.Types.Count, deserialized2.Types.Count); var origClass = originalAst.Types[0] as ClassDeclaration; var deserClass = deserialized2.Types[0] as ClassDeclaration; Assert.Equal(origClass!.Name, deserClass!.Name); Assert.Equal(origClass.TypeParameters.Count, deserClass.TypeParameters.Count); Assert.Equal(origClass.Members.Count, deserClass.Members.Count); } } }