package me.tomassetti.turin.compiler.relations; import com.google.common.collect.ImmutableList; import jdk.nashorn.internal.ir.Block; import me.tomassetti.turin.classloading.ClassFileDefinition; import me.tomassetti.turin.classloading.TurinClassLoader; import me.tomassetti.turin.compiler.AbstractCompilerTest; import me.tomassetti.turin.compiler.Compiler; import me.tomassetti.turin.parser.Parser; import me.tomassetti.turin.parser.ast.TurinFile; import me.tomassetti.turin.parser.ast.expressions.Expression; import me.tomassetti.turin.parser.ast.invokables.FunctionDefinitionNode; import me.tomassetti.turin.parser.ast.statements.BlockStatement; import me.tomassetti.turin.parser.ast.statements.ReturnStatement; import me.tomassetti.turin.parser.ast.statements.Statement; import me.tomassetti.turin.resolvers.ResolverRegistry; import me.tomassetti.turin.resolvers.SymbolResolver; import me.tomassetti.turin.typesystem.TypeUsage; import org.junit.Test; import turin.relations.OneToManyRelation; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; public class RelationsCompilationTest extends AbstractCompilerTest { @Test public void aClassWithTheCorrectNameIsGeneratedForARelation() throws IOException { TurinFile turinFile = new Parser().parse(this.getClass().getResourceAsStream("/relations/simple_relation.to")); // generate bytecode me.tomassetti.turin.compiler.Compiler.Options options = new Compiler.Options(); Compiler instance = new Compiler(getResolverFor(turinFile), options); List<ClassFileDefinition> classDefinitions = instance.compile(turinFile, new MyErrorCollector()); assertEquals(2, classDefinitions.size()); assertEquals("relations.Relation_Ast", classDefinitions.get(1).getName()); } @Test public void theGeneratedRelationClassHasStaticFieldNeeded() throws IOException, IllegalAccessException { TurinFile turinFile = new Parser().parse(this.getClass().getResourceAsStream("/relations/simple_relation.to")); // generate bytecode me.tomassetti.turin.compiler.Compiler.Options options = new Compiler.Options(); Compiler instance = new Compiler(getResolverFor(turinFile), options); List<ClassFileDefinition> classDefinitions = instance.compile(turinFile, new MyErrorCollector()); assertEquals(2, classDefinitions.size()); Class astClass = new TurinClassLoader().addClass(classDefinitions.get(1)); assertEquals(1, astClass.getFields().length); Field field = astClass.getFields()[0]; assertEquals("RELATION", field.getName()); assertTrue(Modifier.isStatic(field.getModifiers())); assertTrue(Modifier.isFinal(field.getModifiers())); Object value = field.get(null); assertNotNull(value); assertEquals(OneToManyRelation.class.getCanonicalName(), value.getClass().getCanonicalName()); } @Test public void theGeneratedRelationClassHasMethodsForAccessingEndpoints() throws IOException, NoSuchMethodException { TurinFile turinFile = new Parser().parse(this.getClass().getResourceAsStream("/relations/simple_relation.to")); // generate bytecode me.tomassetti.turin.compiler.Compiler.Options options = new Compiler.Options(); Compiler instance = new Compiler(getResolverFor(turinFile), options); List<ClassFileDefinition> classDefinitions = instance.compile(turinFile, new MyErrorCollector()); assertEquals(2, classDefinitions.size()); TurinClassLoader turinClassLoader = new TurinClassLoader(); Class nodeClass = turinClassLoader.addClass(classDefinitions.get(0)); Class astClass = turinClassLoader.addClass(classDefinitions.get(1)); assertEquals(2, astClass.getDeclaredMethods().length); Method parentForChildrenElement = astClass.getDeclaredMethod("parentForChildrenElement", new Class[]{nodeClass}); Method childrenForParent = astClass.getDeclaredMethod("childrenForParent", new Class[]{nodeClass}); } /* @Test public void aRelationSubsetIsGeneratedCorrectly() throws IOException { TurinFile turinFile = new Parser().parse(this.getClass().getResourceAsStream("/relations/relation_subset.to")); // generate bytecode me.tomassetti.turin.compiler.Compiler.Options options = new Compiler.Options(); Compiler instance = new Compiler(getResolverFor(turinFile), options); List<ClassFileDefinition> classDefinitions = instance.compile(turinFile, new MyErrorCollector()); assertEquals(5, classDefinitions.size()); assertEquals("relations.Relation_Ast", classDefinitions.get(2).getName()); }*/ /** * We verify if we can handle correctly the type parameters present in the relation endpoints. * @throws IOException * @throws NoSuchMethodException * @throws InvocationTargetException * @throws IllegalAccessException * @throws InstantiationException */ @Test public void relationsReturnCorrectType() throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { TurinFile turinFile = new Parser().parse(this.getClass().getResourceAsStream("/relations/relation_usage.to")); SymbolResolver resolver = getResolverFor(turinFile); ResolverRegistry.INSTANCE.record(turinFile, resolver); FunctionDefinitionNode foo2 = turinFile.getTopLevelFunctionDefinitions().get(1); assertEquals("foo2", foo2.getName()); BlockStatement body = (BlockStatement) foo2.getBody(); Statement lastStatement = body.getStatements().get(body.getStatements().size() - 1); ReturnStatement returnStatement = (ReturnStatement)lastStatement; // Probably JavaAssistTypeDefinition already replaced Type Variables Expression returnedValue = returnStatement.getValue(); TypeUsage returnedValueType = returnedValue.calcType(); assertTrue(returnedValueType.isReferenceTypeUsage()); assertEquals("relations.Node", returnedValueType.asReferenceTypeUsage().getQualifiedName()); } @Test public void relationUsage() throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException { TurinFile turinFile = new Parser().parse(this.getClass().getResourceAsStream("/relations/relation_usage.to")); // generate bytecode me.tomassetti.turin.compiler.Compiler.Options options = new Compiler.Options(); Compiler instance = new Compiler(getResolverFor(turinFile), options); List<ClassFileDefinition> classDefinitions = instance.compile(turinFile, new MyErrorCollector()); assertEquals(6, classDefinitions.size()); TurinClassLoader classLoader = new TurinClassLoader(); Class nodeClass = classLoader.addClass(classDefinitions.get(0)); Class astClass = classLoader.addClass(classDefinitions.get(1)); saveClassFile(classDefinitions.get(1), "relations"); Class foo1 = classLoader.addClass(classDefinitions.get(2)); saveClassFile(classDefinitions.get(2), "relations"); Class foo2 = classLoader.addClass(classDefinitions.get(3)); saveClassFile(classDefinitions.get(3), "relations"); Class foo3 = classLoader.addClass(classDefinitions.get(4)); saveClassFile(classDefinitions.get(4), "relations"); Class foo4 = classLoader.addClass(classDefinitions.get(5)); // foo1 should gives true Object res1 = foo1.getMethod("invoke", new Class[]{}).invoke(null); assertEquals(true, res1); // foo2 should gives Node("C") Object res2 = foo2.getMethod("invoke", new Class[]{}).invoke(null); Object nodeC = nodeClass.getConstructor(String.class).newInstance("C"); assertEquals(nodeC, res2); // foo3 should gives [] Object res3 = foo3.getMethod("invoke", new Class[]{}).invoke(null); assertEquals(Collections.emptyList(), res3); // foo4 should gives [Node("A")] Object nodeA = nodeClass.getConstructor(String.class).newInstance("A"); Object res4 = foo4.getMethod("invoke", new Class[]{}).invoke(null); assertEquals(ImmutableList.of(nodeA), res4); } }