package spoon.test.signature; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.junit.Assert; import org.junit.Test; import spoon.Launcher; import spoon.SpoonModelBuilder; import spoon.compiler.SpoonResourceHelper; import spoon.reflect.code.CtAssignment; import spoon.reflect.code.CtBlock; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtInvocation; import spoon.reflect.code.CtLiteral; import spoon.reflect.code.CtReturn; import spoon.reflect.code.CtStatement; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtType; import spoon.reflect.factory.Factory; import spoon.reflect.factory.FactoryImpl; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.visitor.Query; import spoon.reflect.visitor.filter.NameFilter; import spoon.reflect.visitor.filter.ReferenceTypeFilter; import spoon.reflect.visitor.filter.TypeFilter; import spoon.support.DefaultCoreFactory; import spoon.support.StandardEnvironment; import spoon.support.comparator.DeepRepresentationComparator; import spoon.support.compiler.jdt.JDTSnippetCompiler; public class SignatureTest { @Test public void testNullSignature() throws Exception { // bug found by Thomas Vincent et Mathieu Schepens (students at the // University of Lille) on Nov 4 2014 // in their analysis, they put CtExpressions in a Map // if one expression has an empty signature, an exception is thrown // the solution is to improve the signature of null literals Factory factory = new Launcher().createFactory(); CtClass<?> clazz = factory .Code() .createCodeSnippetStatement( "" + "class X {" + "public Object foo() {" + " return null;" + "}};").compile(); CtReturn<?> returnEl = clazz.getElements( new TypeFilter<>(CtReturn.class)).get(0); CtExpression<?> lit = returnEl.getReturnedExpression(); assertTrue(lit instanceof CtLiteral); assertEquals("null", lit.toString()); // since the signature is null, CtElement.equals throws an exception and // should not CtLiteral<?> lit2 = ((CtLiteral<?>) lit).clone(); HashSet<CtExpression<?>> s = new HashSet<CtExpression<?>>(); s.add(lit); s.add(lit2); } @Test public void testNullSignatureInUnboundVariable() throws Exception { //Unbound variable access bug fix: //Bug description: The signature printer ignored the element Unbound variable reference //(as well all Visitor that extend CtVisitor) //Fix description: modify CtVisitor (including SignaturePrinter) for visiting unbound variable access. Factory factory = new Launcher().createFactory(); // We want to compile a class with an reference to a class that is not // in the classpath // As consequence, we set the option NoClasspath as true. factory.getEnvironment().setNoClasspath(true); String unboundVarAccess = "Complex.I"; String content = "" + "class X {" + "public Object foo(java.util.List<String> l) {" + " Integer.toString(" + unboundVarAccess + ");" + " return null;" + "}};"; SpoonModelBuilder builder = new JDTSnippetCompiler(factory, content); try { builder.build(); Assert.fail(); } catch (Exception e) { // Must fail due to the unbound element "Complex.I" } CtClass<?> clazz1 = (CtClass<?>) factory.Type().getAll().get(0); Set<CtMethod<?>> methods = clazz1.getMethods(); CtMethod<?> method = (CtMethod<?>) methods.toArray()[0]; assertEquals("java.lang.Object foo(java.util.List)", method.getSignature()); CtInvocation<?> invo = (CtInvocation<?>) method.getBody().getStatement(0); CtExpression<?> argument1 = invo.getArguments().get(0); assertEquals(unboundVarAccess, argument1.toString()); } @Test public void testLiteralSignature(){ Factory factory = new FactoryImpl(new DefaultCoreFactory(), new StandardEnvironment()); CtStatement sta1 = (factory).Code().createCodeSnippetStatement("System.out.println(\"hello\")") .compile(); CtStatement sta2 = (factory).Code().createCodeSnippetStatement("String hello =\"t1\"; System.out.println(hello)") .compile(); CtStatement sta2bis = ((CtBlock<?>)sta2.getParent()).getStatement(1); assertFalse(sta1.equals(sta2bis));// equals depends on deep equality String parameterWithQuotes = ((CtInvocation<?>)sta1).getArguments().get(0).toString(); assertEquals("\"hello\"",parameterWithQuotes); (factory).Code().createCodeSnippetStatement("Integer.toBinaryString(20)") .compile(); } @Test public void testMethodInvocationSignatureStaticFieldsVariables(){ Factory factory = new FactoryImpl(new DefaultCoreFactory(), new StandardEnvironment()); CtStatement sta1 = (factory).Code().createCodeSnippetStatement("Integer.toBinaryString(Integer.MAX_VALUE)") .compile(); CtStatement sta2 = (factory).Code().createCodeSnippetStatement("Integer.toBinaryString(Integer.MIN_VALUE)") .compile(); String signature1 = ((CtInvocation)sta1).getExecutable().getSignature(); String signature2 = ((CtInvocation)sta2).getExecutable().getSignature(); assertEquals(signature1, signature2); assertFalse(sta1.equals(sta2)); CtStatement stb1 = (factory).Code().createCodeSnippetStatement("Integer.toBinaryString(20)") .compile(); CtStatement stb2 = (factory).Code().createCodeSnippetStatement("Integer.toBinaryString(30)") .compile(); String signature1b = ((CtInvocation)sta1).getExecutable().getSignature(); String signature2b = ((CtInvocation)sta2).getExecutable().getSignature(); assertEquals(signature1b, signature2b); assertFalse(stb1.equals(stb2)); CtStatement stc1 = (factory).Code().createCodeSnippetStatement("String.format(\"format1\",\"f2\" )") .compile(); CtStatement stc2 = (factory).Code().createCodeSnippetStatement("String.format(\"format2\",\"f2\" )") .compile(); String signaturestc1 = ((CtInvocation)sta1).getExecutable().getSignature(); String signaturestc2 = ((CtInvocation)sta2).getExecutable().getSignature(); assertEquals(signaturestc1, signaturestc2); assertFalse(stc1.equals(stc2)); } @Test public void testMethodInvocationSignatureWithVariableAccess() throws Exception{ Factory factory = new FactoryImpl(new DefaultCoreFactory(), new StandardEnvironment()); factory.getEnvironment().setNoClasspath(true); String content = "" + "class PR {" + "static String PRS = null;" + "public Object foo(String p) {" + " int s = 0; " + " this.foo(s);" + "this.foo(p);" + " return null;" + "}" + " public Object foo(int p) {" + " String s = null;" + " this.foo(s);" + "this.foo(p);" + "return null;" + "}" + "};"; SpoonModelBuilder builder = new JDTSnippetCompiler(factory, content); builder.build(); CtClass<?> clazz1 = (CtClass<?>) factory.Type().getAll().get(0); assertNotNull(clazz1); //**FIRST PART: passing local variable access. ///--------From the first method we take the method invocations TreeSet<CtMethod<?>> ts = new TreeSet<CtMethod<?>>(new DeepRepresentationComparator()); ts.addAll(clazz1.getMethods()); CtMethod[] methodArray = ts.toArray(new CtMethod[0]); CtMethod<?> methodInteger = methodArray[0]; assertEquals("java.lang.Object foo(int)", methodInteger.getSignature()); CtInvocation<?> invoToInt1 = (CtInvocation<?>) methodInteger.getBody().getStatement(1); CtExpression<?> argumentToInt1 = invoToInt1.getArguments().get(0); //----------From the second method we take the Method Inv CtMethod<?> methodString = (CtMethod<?>) methodArray[1]; assertEquals("java.lang.Object foo(java.lang.String)", methodString.getSignature()); CtInvocation<?> invoToString = (CtInvocation<?>) methodString.getBody().getStatement(1); CtExpression<?> argumentToString = invoToString.getArguments().get(0); //we compare the signatures of " this.foo(s);" from both methods assertNotEquals(invoToInt1, invoToString); //Now we check that we have two invocation to "foo(s)", //but one invocation is with var 's' type integer, the other var 's' type int assertNotEquals(argumentToInt1, argumentToString); /// ***SECOND PART, passing Parameters CtInvocation<?> invoToString2 = (CtInvocation<?>) methodInteger.getBody().getStatement(2); CtExpression<?> argumentToString2 = invoToString2.getArguments().get(0); CtInvocation<?> invoToInt2 = (CtInvocation<?>) methodString.getBody().getStatement(2); CtExpression<?> argumentToInt2 = invoToInt2.getArguments().get(0); /// //Compare the method invo signature (same real argument's name, different type) assertNotEquals(invoToString2,invoToInt2); //Compare signature of parameters (same var name, different type) assertNotEquals(argumentToString2,argumentToInt2); } @Test public void testUnboundFieldSignature(){ Factory factory = new FactoryImpl(new DefaultCoreFactory(), new StandardEnvironment()); factory.getEnvironment().setNoClasspath(true); String content = "" + "class PR {" + "public java.io.File foo(String p) {" + " this.mfield = p; " + " return null;" + "}" + "};"; SpoonModelBuilder builder = new JDTSnippetCompiler(factory, content); try{ builder.build(); fail(); } catch(Exception e){ //must fail } CtClass<?> clazz1 = (CtClass<?>) factory.Type().getAll().get(0); assertNotNull(clazz1); //**FIRST PART: passing local variable access. ///--------From the first method we take the method invocations CtMethod<?> methodString = (CtMethod<?>) clazz1.getMethods().toArray()[0]; assertEquals("java.io.File foo(java.lang.String)", methodString.getSignature()); CtAssignment<?,?> invoToInt1 = (CtAssignment<?,?>) methodString.getBody().getStatement(0); CtExpression<?> left = invoToInt1.getAssigned(); assertEquals("this.mfield",left.toString()); assertEquals(null,left.getType());// null because noclasspath assertEquals("this.mfield = p",invoToInt1.toString()); } @Test public void testArgumentNotNullForExecutableReference() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] {"--output-type", "nooutput" }); launcher.addInputResource("./src/test/resources/variable/PropPanelUseCase_1.40.java"); launcher.getEnvironment().setNoClasspath(true); launcher.run(); final List<CtExecutableReference> references = Query.getElements(launcher.getFactory(), new ReferenceTypeFilter<CtExecutableReference>(CtExecutableReference.class) { @Override public boolean matches(CtExecutableReference reference) { return "addField".equals(reference.getSimpleName()) && super.matches(reference); } }); assertEquals("#addField(<unknown>, <unknown>)", references.get(0).getSignature()); assertEquals("#addField(<unknown>, org.argouml.uml.ui.UMLComboBoxNavigator)", references.get(1).getSignature()); for (CtExecutableReference reference : references) { assertNotEquals("#addField(null, null)", reference.getSignature()); } } @Test public void testBugSignature() throws Exception { // contract: two methods with same name and return type yet different argument types // must have different signatures Launcher launcher = new Launcher(); launcher.getEnvironment().setNoClasspath(true); SpoonModelBuilder comp = launcher.createCompiler(); comp.addInputSources(SpoonResourceHelper.resources("./src/main/java/spoon/SpoonModelBuilder.java")); comp.build(); CtType<?> ctClass = (CtType<?>) comp.getFactory().Type().get(SpoonModelBuilder.class); List<CtMethod<?>> methods = ctClass.getElements(new NameFilter<CtMethod<?>>("addInputSource")); assertEquals(2, methods.size()); CtMethod<?> method = methods.get(0); assertEquals( "void addInputSource(java.io.File)", method.getSignature()); CtMethod<?> method2 = methods.get(1); assertEquals( "void addInputSource(spoon.compiler.SpoonResource)", method2.getSignature()); assertNotEquals(method, method2); } }