package spoon.test.imports; import org.junit.Test; import spoon.Launcher; import spoon.SpoonException; import spoon.SpoonModelBuilder; import spoon.compiler.SpoonResource; import spoon.compiler.SpoonResourceHelper; import spoon.reflect.CtModel; import spoon.reflect.code.CtConstructorCall; import spoon.reflect.code.CtInvocation; import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtStatement; import spoon.reflect.code.CtThisAccess; import spoon.reflect.code.CtTypeAccess; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtType; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtExecutableReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.ImportScanner; import spoon.reflect.visitor.ImportScannerImpl; import spoon.reflect.visitor.PrettyPrinter; import spoon.reflect.visitor.Query; import spoon.reflect.visitor.chain.CtScannerListener; import spoon.reflect.visitor.chain.ScanningMode; import spoon.reflect.visitor.filter.NameFilter; import spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction; import spoon.reflect.visitor.filter.TypeFilter; import spoon.support.comparator.CtLineElementComparator; import spoon.support.util.SortedList; import spoon.test.imports.testclasses.A; import spoon.test.imports.testclasses.ClassWithInvocation; import spoon.test.imports.testclasses.ClientClass; import spoon.test.imports.testclasses.Mole; import spoon.test.imports.testclasses.NotImportExecutableType; import spoon.test.imports.testclasses.Pozole; import spoon.test.imports.testclasses.SubClass; import spoon.test.imports.testclasses.Tacos; import spoon.test.imports.testclasses.internal.ChildClass; import spoon.testing.utils.ModelUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import static spoon.testing.utils.ModelUtils.buildClass; import static spoon.testing.utils.ModelUtils.canBeBuilt; public class ImportTest { @Test public void testImportOfAnInnerClassInASuperClassPackageAutoImport() throws Exception { Launcher spoon = new Launcher(); spoon.getEnvironment().setShouldCompile(true); spoon.getEnvironment().setAutoImports(true); spoon.addInputResource("./src/test/java/spoon/test/imports/testclasses/internal/SuperClass.java"); spoon.addInputResource("./src/test/java/spoon/test/imports/testclasses/internal/ChildClass.java"); spoon.addInputResource("./src/test/java/spoon/test/imports/testclasses/ClientClass.java"); spoon.setBinaryOutputDirectory("./target/spoon/super_imports/bin"); spoon.setSourceOutputDirectory("./target/spoon/super_imports/src"); spoon.run(); final List<CtClass<?>> classes = Query.getElements(spoon.getFactory(), new NameFilter<CtClass<?>>("ClientClass")); final CtClass<?> innerClass = classes.get(0).getNestedType("InnerClass"); String expected = "spoon.test.imports.testclasses.ClientClass.InnerClass"; assertEquals(expected, innerClass.getReference().toString()); //test that acces path depends on the context //this checks the access path in context of innerClass. The context is defined by CtTypeReference.getParent(CtType.class). assertEquals("spoon.test.imports.testclasses.internal.ChildClass.InnerClassProtected", innerClass.getSuperclass().toString()); //this checks the access path in context of SuperClass. The context is defined by CtTypeReference.getParent(CtType.class) assertEquals("spoon.test.imports.testclasses.internal.SuperClass.InnerClassProtected", innerClass.getSuperclass().getTypeDeclaration().getReference().toString()); assertEquals("InnerClassProtected", innerClass.getSuperclass().getSimpleName()); assertEquals("SuperClass", innerClass.getSuperclass().getDeclaringType().getSimpleName()); assertEquals(spoon.getFactory().Class().get("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected"), innerClass.getSuperclass().getDeclaration()); } @Test public void testImportOfAnInnerClassInASuperClassPackageFullQualified() throws Exception { Launcher spoon = new Launcher(); spoon.getEnvironment().setShouldCompile(true); spoon.getEnvironment().setAutoImports(false); spoon.addInputResource("./src/test/java/spoon/test/imports/testclasses/internal/SuperClass.java"); spoon.addInputResource("./src/test/java/spoon/test/imports/testclasses/internal/ChildClass.java"); spoon.addInputResource("./src/test/java/spoon/test/imports/testclasses/ClientClass.java"); spoon.setBinaryOutputDirectory("./target/spoon/super_imports/bin"); spoon.setSourceOutputDirectory("./target/spoon/super_imports/src"); spoon.run(); final List<CtClass<?>> classes = Query.getElements(spoon.getFactory(), new NameFilter<CtClass<?>>("ClientClass")); final CtClass<?> innerClass = classes.get(0).getNestedType("InnerClass"); assertEquals("spoon.test.imports.testclasses.ClientClass$InnerClass", innerClass.getQualifiedName()); String expected = "spoon.test.imports.testclasses.ClientClass.InnerClass"; assertEquals(expected, innerClass.getReference().toString()); assertEquals("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected", innerClass.getSuperclass().getQualifiedName()); expected = "spoon.test.imports.testclasses.internal.ChildClass.InnerClassProtected"; assertEquals(expected, innerClass.getSuperclass().toString()); assertEquals("SuperClass", innerClass.getSuperclass().getDeclaringType().getSimpleName()); assertEquals(spoon.getFactory().Class().get("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected"), innerClass.getSuperclass().getDeclaration()); } @Test public void testImportOfAnInnerClassInASuperClassAvailableInLibrary() throws Exception { SpoonModelBuilder comp = new Launcher().createCompiler(); List<SpoonResource> fileToBeSpooned = SpoonResourceHelper.resources("./src/test/resources/visibility/YamlRepresenter.java"); assertEquals(1, fileToBeSpooned.size()); comp.addInputSources(fileToBeSpooned); List<SpoonResource> classpath = SpoonResourceHelper.resources("./src/test/resources/visibility/snakeyaml-1.9.jar"); assertEquals(1, classpath.size()); comp.setSourceClasspath(classpath.get(0).getPath()); comp.build(); Factory factory = comp.getFactory(); CtType<?> theClass = factory.Type().get("visibility.YamlRepresenter"); final CtClass<?> innerClass = theClass.getNestedType("RepresentConfigurationSection"); String expected = "visibility.YamlRepresenter.RepresentConfigurationSection"; assertEquals(expected, innerClass.getReference().toString()); expected = "org.yaml.snakeyaml.representer.Representer.RepresentMap"; assertEquals(expected, innerClass.getSuperclass().toString()); } @Test public void testImportOfAnInnerClassInAClassPackage() throws Exception { Launcher spoon = new Launcher(); spoon.setArgs(new String[] {"--output-type", "nooutput" }); Factory factory = spoon.createFactory(); SpoonModelBuilder compiler = spoon.createCompiler(factory, SpoonResourceHelper .resources("./src/test/java/spoon/test/imports/testclasses/internal/PublicSuperClass.java", "./src/test/java/spoon/test/imports/testclasses/DefaultClientClass.java")); compiler.build(); final CtClass<?> client = (CtClass<?>) factory.Type().get("spoon.test.imports.testclasses.DefaultClientClass"); final CtMethod<?> methodVisit = client.getMethodsByName("visit").get(0); final CtType<Object> innerClass = factory.Type().get("spoon.test.imports.testclasses.DefaultClientClass$InnerClass"); assertEquals("Type of the method must to be InnerClass accessed via DefaultClientClass.", innerClass, methodVisit.getType().getDeclaration()); } @Test public void testNewInnerClassDefinesInItsClassAndSuperClass() throws Exception { Launcher spoon = new Launcher(); spoon.setArgs(new String[] {"--output-type", "nooutput" }); Factory factory = spoon.createFactory(); SpoonModelBuilder compiler = spoon.createCompiler(factory, SpoonResourceHelper.resources("./src/test/java/spoon/test/imports/testclasses/SuperClass.java", "./src/test/java/spoon/test/imports/testclasses/SubClass.java")); compiler.build(); final CtClass<?> subClass = (CtClass<?>) factory.Type().get(SubClass.class); final CtConstructorCall<?> ctConstructorCall = subClass.getElements(new TypeFilter<CtConstructorCall<?>>(CtConstructorCall.class)).get(0); assertEquals("new spoon.test.imports.testclasses.SubClass.Item(\"\")", ctConstructorCall.toString()); final String expected = "public class SubClass extends spoon.test.imports.testclasses.SuperClass {" + System.lineSeparator() + " public void aMethod() {" + System.lineSeparator() + " new spoon.test.imports.testclasses.SubClass.Item(\"\");" + System.lineSeparator() + " }" + System.lineSeparator() + System.lineSeparator() + " public static class Item extends spoon.test.imports.testclasses.SuperClass.Item {" + System.lineSeparator() + " public Item(java.lang.String s) {" + System .lineSeparator() + " super(1, s);" + System.lineSeparator() + " }" + System.lineSeparator() + " }" + System.lineSeparator() + "}"; assertEquals(expected, subClass.toString()); } @Test public void testMissingImport() throws Exception { Launcher spoon = new Launcher(); spoon.setArgs(new String[] {"--output-type", "nooutput" }); Factory factory = spoon.createFactory(); factory.getEnvironment().setNoClasspath(true); factory.getEnvironment().setLevel("OFF"); SpoonModelBuilder compiler = spoon.createCompiler(factory, SpoonResourceHelper.resources("./src/test/resources/import-resources/fr/inria/MissingImport.java")); compiler.build(); CtTypeReference<?> type = factory.Class().getAll().get(0).getFields().get(0).getType(); assertEquals("Abcd", type.getSimpleName()); assertEquals("fr.inria.internal", type.getPackage().getSimpleName()); } @Test public void testAnotherMissingImport() throws Exception { Launcher spoon = new Launcher(); spoon.setArgs(new String[] {"--output-type", "nooutput" }); Factory factory = spoon.createFactory(); factory.getEnvironment().setNoClasspath(true); factory.getEnvironment().setLevel("OFF"); SpoonModelBuilder compiler = spoon.createCompiler(factory, SpoonResourceHelper.resources("./src/test/resources/import-resources/fr/inria/AnotherMissingImport.java")); compiler.build(); List<CtMethod<?>> methods = factory.getModel().getElements(new NameFilter<CtMethod<?>>("doSomething")); CtTypeReference<?> type = methods.get(0).getParameters().get(0).getType(); assertEquals("SomeType", type.getSimpleName()); assertEquals("externallib", type.getPackage().getSimpleName()); CtMethod<?> mainMethod = factory.Class().getAll().get(0).getMethodsByName("main").get(0); List<CtStatement> statements = mainMethod.getBody().getStatements(); CtStatement invocationStatement = statements.get(1); assertTrue(invocationStatement instanceof CtInvocation); CtInvocation invocation = (CtInvocation) invocationStatement; CtExecutableReference executableReference = invocation.getExecutable(); assertEquals("fr.inria.AnotherMissingImport#doSomething(externallib.SomeType)", executableReference.getSignature()); assertSame(methods.get(0), executableReference.getDeclaration()); } @Test public void testSpoonWithImports() throws Exception { final Launcher launcher = new Launcher(); launcher.run(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses", "--output-type", "nooutput", "--with-imports" }); final CtClass<ImportTest> aClass = launcher.getFactory().Class().get(ChildClass.class); final CtClass<ImportTest> anotherClass = launcher.getFactory().Class().get(ClientClass.class); final CtClass<ImportTest> classWithInvocation = launcher.getFactory().Class().get(ClassWithInvocation.class); final ImportScanner importScanner = new ImportScannerImpl(); final Collection<CtTypeReference<?>> imports = importScanner.computeImports(aClass); assertEquals(2, imports.size()); final Collection<CtTypeReference<?>> imports1 = importScanner.computeImports(anotherClass); //ClientClass needs 2 imports: ChildClass, PublicInterface2 assertEquals(2, imports1.size()); //check that printer did not used the package protected class like "SuperClass.InnerClassProtected" assertTrue(anotherClass.toString().indexOf("InnerClass extends ChildClass.InnerClassProtected")>0); final Collection<CtTypeReference<?>> imports2 = importScanner.computeImports(classWithInvocation); assertEquals("Spoon ignores the arguments of CtInvocations", 1, imports2.size()); } @Test public void testStaticImportForInvocationInNoClasspath() throws Exception { final Launcher launcher = new Launcher(); launcher.run(new String[] { "-i", "./src/test/resources/import-static", "--output-type", "nooutput", "--noclasspath" }); final List<CtInvocation<?>> elements = new SortedList(new CtLineElementComparator()); elements.addAll(Query.getElements(launcher.getFactory(), new TypeFilter<CtInvocation<?>>(CtInvocation.class) { @Override public boolean matches(CtInvocation<?> element) { return !element.getExecutable().isConstructor() && super.matches(element); } })); // Invocation for a static method with the declaring class specified. assertCorrectInvocation(new Expected().name("staticMethod").target("A").declaringType("A").typeIsNull(true), elements.get(0)); // Invocation for a static method without the declaring class specified. assertCorrectInvocation(new Expected().name("staticMethod").target("pack1.A").declaringType("A").typeIsNull(true), elements.get(1)); // Invocation for a static method with the declaring class specified and a return type. assertCorrectInvocation(new Expected().name("staticMethod").target("A").declaringType("A").typeIsNull(false), elements.get(2)); // Invocation for a static method without the declaring class specified and a return type. assertCorrectInvocation(new Expected().name("staticMethod").target("pack1.A").declaringType("A").typeIsNull(false), elements.get(3)); // Invocation for a static method in an inner class with the declaring class specified. assertCorrectInvocation(new Expected().name("makeBurritos").target("Tacos.Burritos").declaringType("Burritos").typeIsNull(false), elements.get(4)); // Invocation for a static method in an inner class without the declaring class specified. assertCorrectInvocation(new Expected().name("makeBurritos").target("Tacos.Burritos").declaringType("Burritos").typeIsNull(true), elements.get(5)); // Invocation for a static method in an inner class with the declaring class specified and a return type. assertCorrectInvocation(new Expected().name("makeBurritos").target("Tacos.Burritos").declaringType("Burritos").typeIsNull(false), elements.get(6)); // Invocation for a static method in an innser class without the declaring class specified and a return type. assertCorrectInvocation(new Expected().name("makeBurritos").target("Tacos.Burritos").declaringType("Burritos").typeIsNull(false), elements.get(7)); // Invocation for a static method in an inner class with the declaring class specified. assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("D").typeIsNull(true), elements.get(8)); // Invocation for a static method in an inner class without the declaring class specified. assertCorrectInvocation(new Expected().name("staticD").target("pack2.C.D").declaringType("D").typeIsNull(true), elements.get(9)); // Invocation for a static method in an inner class with the declaring class specified and a return type. assertCorrectInvocation(new Expected().name("staticD").target("C.D").declaringType("D").typeIsNull(false), elements.get(10)); // Invocation for a static method in an inner class without the declaring class specified and a return type. assertCorrectInvocation(new Expected().name("staticD").target("pack2.C.D").declaringType("D").typeIsNull(false), elements.get(11)); // Invocation for a static method with the declaring class specified and an import *. assertCorrectInvocation(new Expected().name("staticE").target("pack3.E.E").declaringType("E").typeIsNull(true), elements.get(12)); // Invocation for a static method without the declaring class specified and an import *. assertCorrectInvocationWithLimit(new Expected().name("staticE").typeIsNull(true), elements.get(13)); // Invocation for a static method with the declaring class specified, a return type and an import *. assertCorrectInvocation(new Expected().name("staticE").target("pack3.E.E").declaringType("E").typeIsNull(false), elements.get(14)); // Invocation for a static method without the declaring class specified, a return type and an import *. assertCorrectInvocationWithLimit(new Expected().name("staticE").typeIsNull(false), elements.get(15)); } @Test public void testImportOfInvocationOfPrivateClass() throws Exception { final Factory factory = getFactory( "./src/test/java/spoon/test/imports/testclasses/internal2/Chimichanga.java", "./src/test/java/spoon/test/imports/testclasses/Mole.java"); ImportScanner importContext = new ImportScannerImpl(); Collection<CtTypeReference<?>> imports = importContext.computeImports(factory.Class().get(Mole.class)); assertEquals(1, imports.size()); assertEquals("spoon.test.imports.testclasses.internal2.Chimichanga", imports.toArray()[0].toString()); } @Test public void testNotImportExecutableType() throws Exception { final Factory factory = getFactory( "./src/test/java/spoon/test/imports/testclasses/internal3/Foo.java", "./src/test/java/spoon/test/imports/testclasses/internal3/Bar.java", "./src/test/java/spoon/test/imports/testclasses/NotImportExecutableType.java"); ImportScanner importContext = new ImportScannerImpl(); Collection<CtTypeReference<?>> imports = importContext.computeImports(factory.Class().get(NotImportExecutableType.class)); assertEquals(2, imports.size()); Set<String> expectedImports = new HashSet<>( Arrays.asList("spoon.test.imports.testclasses.internal3.Foo", "java.io.File")); Set<String> actualImports = imports.stream().map(CtTypeReference::toString).collect(Collectors.toSet()); assertEquals(expectedImports, actualImports); } @Test public void testImportOfInvocationOfStaticMethod() throws Exception { final Factory factory = getFactory( "./src/test/java/spoon/test/imports/testclasses/internal2/Menudo.java", "./src/test/java/spoon/test/imports/testclasses/Pozole.java"); ImportScanner importContext = new ImportScannerImpl(); Collection<CtTypeReference<?>> imports = importContext.computeImports(factory.Class().get(Pozole.class)); assertEquals(1, imports.size()); assertEquals("spoon.test.imports.testclasses.internal2.Menudo", imports.toArray()[0].toString()); } @Test public void testImportStaticAndFieldAccess() throws Exception { // contract: Qualified field access and an import static should rewrite in fully qualified mode. final Launcher launcher = new Launcher(); launcher.setArgs(new String[] {"--output-type", "nooutput" }); launcher.addInputResource("./src/test/java/spoon/test/imports/testclasses/internal4/"); launcher.addInputResource("./src/test/java/spoon/test/imports/testclasses/Tacos.java"); launcher.buildModel(); final CtType<Object> aTacos = launcher.getFactory().Type().get(Tacos.class); final CtStatement assignment = aTacos.getMethod("m").getBody().getStatement(0); assertTrue(assignment instanceof CtLocalVariable); assertEquals("spoon.test.imports.testclasses.internal4.Constants.CONSTANT.foo", ((CtLocalVariable) assignment).getAssignment().toString()); } @Test public void testFullQualifiedNameImport() throws Exception { String newLine = System.getProperty("line.separator"); Factory factory = ModelUtils.build(A.class); factory.getEnvironment().setAutoImports(true); CtClass<Object> aClass = factory.Class().get(A.class); assertEquals("public class A {" + newLine + " public class ArrayList extends java.util.ArrayList { }" + newLine + "}", aClass.toString()); } @Test public void testAccessToNestedClass() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses", "--with-imports" }); launcher.buildModel(); final CtClass<ImportTest> aClass = launcher.getFactory().Class().get(ClientClass.class.getName()+"$InnerClass"); assertEquals(ClientClass.class.getName()+"$InnerClass", aClass.getQualifiedName()); final CtTypeReference<?> parentClass = aClass.getSuperclass(); //comment next line and parentClass.getActualClass(); will fail anyway assertEquals("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected", parentClass.getQualifiedName()); Class<?> actualClass = parentClass.getActualClass(); assertEquals("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected", actualClass.getName()); } @Test public void testAccessType() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses", "--with-imports" }); launcher.buildModel(); final CtClass<ImportTest> aInnerClass = launcher.getFactory().Class().get(ClientClass.class.getName()+"$InnerClass"); final CtClass<ImportTest> aSuperClass = launcher.getFactory().Class().get("spoon.test.imports.testclasses.internal.SuperClass"); assertEquals(ClientClass.class.getName()+"$InnerClass", aInnerClass.getQualifiedName()); //Check that access type of ClientClass$InnerClass in package protected class is still ClientClass assertEquals(ClientClass.class.getName(), aInnerClass.getReference().getAccessType().getQualifiedName()); final CtTypeReference<?> innerClassProtectedByGetSuperClass = aInnerClass.getSuperclass(); final CtTypeReference<?> innerClassProtectedByQualifiedName = launcher.getFactory().Class().get("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected").getReference(); assertEquals("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected", innerClassProtectedByGetSuperClass.getQualifiedName()); assertEquals("spoon.test.imports.testclasses.internal.SuperClass$InnerClassProtected", innerClassProtectedByQualifiedName.getQualifiedName()); assertEquals("spoon.test.imports.testclasses.internal.ChildClass", innerClassProtectedByGetSuperClass.getAccessType().getQualifiedName()); assertEquals("spoon.test.imports.testclasses.internal.SuperClass", innerClassProtectedByQualifiedName.getAccessType().getQualifiedName()); assertEquals("spoon.test.imports.testclasses.internal.ChildClass.InnerClassProtected", innerClassProtectedByGetSuperClass.toString()); assertEquals("spoon.test.imports.testclasses.internal.SuperClass.InnerClassProtected", innerClassProtectedByQualifiedName.toString()); } @Test public void testCanAccess() throws Exception { class Checker { final Launcher launcher; final CtTypeReference<?> aClientClass; final CtTypeReference<?> anotherClass; Checker() { launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses", "--with-imports" }); launcher.buildModel(); aClientClass = launcher.getFactory().Class().get(ClientClass.class).getReference(); anotherClass = launcher.getFactory().Class().get(Tacos.class).getReference(); } void checkCanAccess(String aClassName, boolean isInterface, boolean canAccessClientClass, boolean canAccessAnotherClass, String clientAccessType, String anotherAccessType) { CtTypeReference<?> target; if(isInterface) { target = launcher.getFactory().Interface().create(aClassName).getReference(); } else { target = launcher.getFactory().Class().get(aClassName).getReference(); } boolean isNested = target.getDeclaringType()!=null; CtTypeReference<?> accessType; target.setParent(aClientClass.getTypeDeclaration()); if(canAccessClientClass) { assertTrue("ClientClass should have access to "+aClassName+" but it has not", aClientClass.canAccess(target)); } else { assertFalse("ClientClass should have NO access to "+aClassName+" but it has", aClientClass.canAccess(target)); } if(isNested) { accessType = target.getAccessType(); if(clientAccessType!=null) { assertEquals(clientAccessType, accessType.getQualifiedName()); } else if(accessType!=null){ fail("ClientClass should have NO accessType to "+aClassName+" but it has "+accessType.getQualifiedName()); } } target.setParent(anotherClass.getTypeDeclaration()); if(canAccessAnotherClass) { assertTrue("Tacos class should have access to "+aClassName+" but it has not", anotherClass.canAccess(target)); } else { assertFalse("Tacos class should have NO access to "+aClassName+" but it has", anotherClass.canAccess(target)); } if(isNested) { if(anotherAccessType!=null) { accessType = target.getAccessType(); assertEquals(anotherAccessType, accessType.getQualifiedName()); } else { try { accessType = target.getAccessType(); } catch (SpoonException e) { if(e.getMessage().indexOf("Cannot compute access path to type: ")==-1) { throw e; }//else OK, it should throw exception accessType = null; } if(accessType!=null){ fail("Tacos class should have NO accessType to "+aClassName+" but it has "+accessType.getQualifiedName()); } } } } } Checker c = new Checker(); c.checkCanAccess("spoon.test.imports.testclasses.ClientClass", false, true, true, null, null); c.checkCanAccess("spoon.test.imports.testclasses.ClientClass$InnerClass", false, true, false, "spoon.test.imports.testclasses.ClientClass", "spoon.test.imports.testclasses.ClientClass"); c.checkCanAccess("spoon.test.imports.testclasses.internal.ChildClass", false, true, true, null, null); c.checkCanAccess("spoon.test.imports.testclasses.internal.PublicInterface2", true, true, true, null, null); c.checkCanAccess("spoon.test.imports.testclasses.internal.PublicInterface2$NestedInterface", true, true, true, "spoon.test.imports.testclasses.internal.PublicInterface2", "spoon.test.imports.testclasses.internal.PublicInterface2"); c.checkCanAccess("spoon.test.imports.testclasses.internal.PublicInterface2$NestedClass", true, true, true, "spoon.test.imports.testclasses.internal.PublicInterface2", "spoon.test.imports.testclasses.internal.PublicInterface2"); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$PublicInterface", true, true, true, "spoon.test.imports.testclasses.internal.ChildClass", null); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$PackageProtectedInterface", true, false, false, "spoon.test.imports.testclasses.internal.ChildClass", null); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$ProtectedInterface", true, true, false, "spoon.test.imports.testclasses.internal.ChildClass", null); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$ProtectedInterface$NestedOfProtectedInterface", true, true, true/*canAccess, but has no access to accessType*/, "spoon.test.imports.testclasses.internal.SuperClass$ProtectedInterface", null); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$ProtectedInterface$NestedPublicInterface", true, true, true/*canAccess, but has no access to accessType*/, "spoon.test.imports.testclasses.internal.SuperClass$ProtectedInterface", null); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$PublicInterface", true, true, true/*canAccess, but has no access to accessType*/, "spoon.test.imports.testclasses.internal.ChildClass", null); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$PublicInterface$NestedOfPublicInterface", true, true, true/*canAccess, has access to first accessType, but not to full accesspath*/, "spoon.test.imports.testclasses.internal.SuperClass$PublicInterface", "spoon.test.imports.testclasses.internal.SuperClass$PublicInterface"); c.checkCanAccess("spoon.test.imports.testclasses.internal.SuperClass$PublicInterface$NestedPublicInterface", true, true, true/*canAccess, has access to first accessType, but not to full accesspath*/, "spoon.test.imports.testclasses.internal.SuperClass$PublicInterface", "spoon.test.imports.testclasses.internal.SuperClass$PublicInterface"); } @Test public void testNestedAccessPathWithTypedParameter() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses2/AbstractMapBasedMultimap.java" }); launcher.buildModel(); launcher.prettyprint(); try { launcher.getModelBuilder().compile(); } catch (Exception e) { fail(e.getMessage()); } CtClass<?> mm = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.AbstractMapBasedMultimap"); CtClass<?> mmwli = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.AbstractMapBasedMultimap$WrappedList$WrappedListIterator"); assertEquals("private class WrappedListIterator extends spoon.test.imports.testclasses2.AbstractMapBasedMultimap<K, V>.WrappedCollection.WrappedIterator {}",mmwli.toString()); assertTrue(mm.toString().indexOf("AbstractMapBasedMultimap<K, V>.WrappedCollection.WrappedIterator")>=0); CtClass<?> mmwliother = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.AbstractMapBasedMultimap$OtherWrappedList$WrappedListIterator"); assertEquals("private class WrappedListIterator extends spoon.test.imports.testclasses2.AbstractMapBasedMultimap<K, V>.OtherWrappedList.WrappedIterator {}",mmwliother.toString()); } @Test public void testNestedAccessPathWithTypedParameterWithImports() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses2/AbstractMapBasedMultimap.java", "--with-imports" }); launcher.buildModel(); launcher.prettyprint(); try { launcher.getModelBuilder().compile(); } catch (Exception e) { fail(e.getMessage()); } CtClass<?> mm = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.AbstractMapBasedMultimap"); CtClass<?> mmwli = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.AbstractMapBasedMultimap$WrappedList$WrappedListIterator"); assertEquals("private class WrappedListIterator extends AbstractMapBasedMultimap<K, V>.WrappedCollection.WrappedIterator {}",mmwli.toString()); assertTrue(mm.toString().indexOf("AbstractMapBasedMultimap<K, V>.WrappedCollection.WrappedIterator")>=0); CtClass<?> mmwliother = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.AbstractMapBasedMultimap$OtherWrappedList$WrappedListIterator"); assertEquals("private class WrappedListIterator extends AbstractMapBasedMultimap<K, V>.OtherWrappedList.WrappedIterator {}",mmwliother.toString()); } @Test public void testNestedStaticPathWithTypedParameter() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses2/Interners.java" }); launcher.buildModel(); launcher.prettyprint(); try { launcher.getModelBuilder().compile(); } catch (Exception e) { fail(e.getMessage()); } CtClass<?> mm = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.Interners"); assertTrue(mm.toString().indexOf("java.util.List<spoon.test.imports.testclasses2.Interners.WeakInterner.Dummy> list;")>=0); } @Test public void testNestedStaticPathWithTypedParameterWithImports() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses2/Interners.java", "--with-imports" }); launcher.buildModel(); launcher.prettyprint(); try { launcher.getModelBuilder().compile(); } catch (Exception e) { fail(e.getMessage()); } CtClass<?> mm = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.Interners"); assertTrue(mm.toString().indexOf("List<Interners.WeakInterner.Dummy> list;")>=0); } @Test public void testDeepNestedStaticPathWithTypedParameter() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses2/StaticWithNested.java" }); launcher.buildModel(); launcher.prettyprint(); try { launcher.getModelBuilder().compile(); } catch (Exception e) { fail(e.getMessage()); } CtClass<?> mm = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.StaticWithNested"); assertTrue("new spoon.test.imports.testclasses2.StaticWithNested.StaticNested.StaticNested2<K>();", mm.toString().indexOf("new spoon.test.imports.testclasses2.StaticWithNested.StaticNested.StaticNested2<K>();")>=0); } @Test public void testDeepNestedStaticPathWithTypedParameterWithImports() throws Exception { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] { "-i", "./src/test/java/spoon/test/imports/testclasses2/StaticWithNested.java", "--with-imports" }); launcher.buildModel(); launcher.prettyprint(); try { launcher.getModelBuilder().compile(); } catch (Exception e) { fail(e.getMessage()); } CtClass<?> mm = launcher.getFactory().Class().get("spoon.test.imports.testclasses2.StaticWithNested"); assertTrue("new StaticWithNested.StaticNested.StaticNested2<K>();", mm.toString().indexOf("new StaticWithNested.StaticNested.StaticNested2<K>();")>=0); } private Factory getFactory(String...inputs) { final Launcher launcher = new Launcher(); launcher.setArgs(new String[] {"--output-type", "nooutput" }); for (String input : inputs) { launcher.addInputResource(input); } launcher.run(); return launcher.getFactory(); } private void assertCorrectInvocation(Expected expected, CtInvocation<?> ctInvocation) { assertEquals(1, ctInvocation.getArguments().size()); assertNotNull(ctInvocation.getTarget()); assertTrue(ctInvocation.getTarget() instanceof CtTypeAccess); assertEquals(expected.target, ctInvocation.getTarget().toString()); assertNotNull(ctInvocation.getExecutable()); assertEquals(expected.name, ctInvocation.getExecutable().getSimpleName()); assertNotNull(ctInvocation.getExecutable().getDeclaringType()); assertEquals(expected.declaringType, ctInvocation.getExecutable().getDeclaringType().getSimpleName()); assertEquals(expected.isNull, ctInvocation.getExecutable().getType() == null); assertEquals(1, ctInvocation.getExecutable().getParameters().size()); } private void assertCorrectInvocationWithLimit(Expected expected, CtInvocation<?> ctInvocation) { assertEquals(1, ctInvocation.getArguments().size()); assertTrue(ctInvocation.getTarget() instanceof CtThisAccess); assertNotNull(ctInvocation.getExecutable()); assertEquals(expected.name, ctInvocation.getExecutable().getSimpleName()); assertNull(ctInvocation.getExecutable().getDeclaringType()); assertEquals(expected.isNull, ctInvocation.getExecutable().getType() == null); assertEquals(1, ctInvocation.getExecutable().getParameters().size()); } private class Expected { String name; String target; String declaringType; boolean isNull; public Expected name(String name) { this.name = name; return this; } public Expected target(String target) { this.target = target; return this; } public Expected declaringType(String declaringType) { this.declaringType = declaringType; return this; } public Expected typeIsNull(boolean isNull) { this.isNull = isNull; return this; } } @Test public void testWithInnerEnumDoesNotImportStaticInnerMethods() { final Launcher launcher = new Launcher(); launcher.getEnvironment().setAutoImports(true); String outputDir = "./target/spooned-innerenum"; launcher.addInputResource("./src/test/java/spoon/test/imports/testclasses/StaticImportsFromEnum.java"); launcher.setSourceOutputDirectory(outputDir); launcher.run(); PrettyPrinter prettyPrinter = launcher.createPrettyPrinter(); CtType element = launcher.getFactory().Class().getAll().get(0); List<CtType<?>> toPrint = new ArrayList<>(); toPrint.add(element); prettyPrinter.calculate(element.getPosition().getCompilationUnit(), toPrint); String output = prettyPrinter.getResult(); assertTrue("The file should not contain a static import to the inner enum method values",!output.contains("import static spoon.test.imports.testclasses.StaticImportsFromEnum$DataElement.values;")); assertTrue("The file should not contain a static import to the inner enum method values of a distinct interface",!output.contains("import static spoon.test.imports.testclasses.ItfWithEnum$Bar.values;")); assertTrue("The file should not contain a static import to the inner enum value",!output.contains("import static spoon.test.imports.testclasses.ItfWithEnum$Bar.Lip;")); canBeBuilt(outputDir, 7); } @Test public void testShouldNotCreateAutoreference() { final Launcher launcher = new Launcher(); launcher.getEnvironment().setAutoImports(false); String outputDir = "./target/spooned-autoref"; launcher.addInputResource("./src/test/java/spoon/test/imports/testclasses/ShouldNotAutoreference.java"); launcher.setSourceOutputDirectory(outputDir); launcher.run(); PrettyPrinter prettyPrinter = launcher.createPrettyPrinter(); CtType element = launcher.getFactory().Class().getAll().get(0); List<CtType<?>> toPrint = new ArrayList<>(); toPrint.add(element); prettyPrinter.calculate(element.getPosition().getCompilationUnit(), toPrint); String output = prettyPrinter.getResult(); assertTrue("The file should not contain a static import for NOFOLLOW_LINKS",!output.contains("import static java.nio.file.LinkOption.NOFOLLOW_LINKS;")); canBeBuilt(outputDir, 7); } @Test public void testAccessPath() { final Launcher launcher = new Launcher(); launcher.addInputResource("./src/test/java/spoon/test/imports/testclasses/TransportIndicesShardStoresAction.java"); String outputDir = "./target/spooned-accessPath"; launcher.setSourceOutputDirectory(outputDir); launcher.run(); CtType element = launcher.getFactory().Class().getAll().get(0); PrettyPrinter prettyPrinter = launcher.createPrettyPrinter(); List<CtType<?>> toPrint = new ArrayList<>(); toPrint.add(element); prettyPrinter.calculate(element.getPosition().getCompilationUnit(), toPrint); String output = prettyPrinter.getResult(); canBeBuilt(outputDir, 7); } @Test public void testSuperInheritanceHierarchyFunction() throws Exception { CtType<?> clientClass = (CtClass<?>) ModelUtils.buildClass(ClientClass.class); CtTypeReference<?> childClass = clientClass.getSuperclass(); CtTypeReference<?> superClass = childClass.getSuperclass(); List<String> result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true)).map(e->{ assertTrue(e instanceof CtType); return ((CtType)e).getQualifiedName(); }).list(); //contract: includingSelf(true) should return input type too assertTrue(result.contains(clientClass.getQualifiedName())); assertTrue(result.contains(childClass.getQualifiedName())); assertTrue(result.contains(superClass.getQualifiedName())); assertTrue(result.contains(Object.class.getName())); result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(false)).map(e->{ assertTrue(e instanceof CtType); return ((CtType)e).getQualifiedName(); }).list(); //contract: includingSelf(false) should return input type too assertFalse(result.contains(clientClass.getQualifiedName())); assertTrue(result.contains(childClass.getQualifiedName())); assertTrue(result.contains(superClass.getQualifiedName())); assertTrue(result.contains(Object.class.getName())); //contract: returnTypeReferences(true) returns CtTypeReferences result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true).returnTypeReferences(true)).map(e->{ assertTrue(e instanceof CtTypeReference); return ((CtTypeReference)e).getQualifiedName(); }).list(); //contract: includingSelf(false) should return input type too assertTrue(result.contains(clientClass.getQualifiedName())); assertTrue(result.contains(childClass.getQualifiedName())); assertTrue(result.contains(superClass.getQualifiedName())); assertTrue(result.contains(Object.class.getName())); //contract: the mapping can be started on type reference too result = clientClass.getReference().map(new SuperInheritanceHierarchyFunction().includingSelf(true).returnTypeReferences(true)).map(e->{ assertTrue(e instanceof CtTypeReference); return ((CtTypeReference)e).getQualifiedName(); }).list(); //contract: includingSelf(false) should return input type too assertTrue(result.contains(clientClass.getQualifiedName())); assertTrue(result.contains(childClass.getQualifiedName())); assertTrue(result.contains(superClass.getQualifiedName())); assertTrue(result.contains(Object.class.getName())); //contract: super type of Object is nothing List<CtTypeReference<?>> typeResult = clientClass.getFactory().Type().OBJECT.map(new SuperInheritanceHierarchyFunction().includingSelf(false).returnTypeReferences(true)).list(); assertEquals(0, typeResult.size()); typeResult = clientClass.getFactory().Type().OBJECT.map(new SuperInheritanceHierarchyFunction().includingSelf(true).returnTypeReferences(true)).list(); assertEquals(1, typeResult.size()); assertEquals(clientClass.getFactory().Type().OBJECT, typeResult.get(0)); } @Test public void testSuperInheritanceHierarchyFunctionListener() throws Exception { CtType<?> clientClass = (CtClass<?>) ModelUtils.buildClass(ClientClass.class); CtTypeReference<?> childClass = clientClass.getSuperclass(); CtTypeReference<?> superClass = childClass.getSuperclass(); //contract: the enter and exit are always called with CtTypeReference instance List<String> result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true).setListener(new CtScannerListener() { @Override public ScanningMode enter(CtElement element) { assertTrue(element instanceof CtTypeReference); return ScanningMode.NORMAL; } @Override public void exit(CtElement element) { assertTrue(element instanceof CtTypeReference); } })).map(e->{ assertTrue(e instanceof CtType); return ((CtType)e).getQualifiedName(); }).list(); assertTrue(result.contains(clientClass.getQualifiedName())); assertTrue(result.contains(childClass.getQualifiedName())); assertTrue(result.contains(superClass.getQualifiedName())); assertTrue(result.contains(Object.class.getName())); //contract: if listener skips ALL, then skipped element and all super classes are not returned result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true).setListener(new CtScannerListener() { @Override public ScanningMode enter(CtElement element) { assertTrue(element instanceof CtTypeReference); if(superClass.getQualifiedName().equals(((CtTypeReference<?>)element).getQualifiedName())) { return ScanningMode.SKIP_ALL; } return ScanningMode.NORMAL; } @Override public void exit(CtElement element) { assertTrue(element instanceof CtTypeReference); } })).map(e->{ assertTrue(e instanceof CtType); return ((CtType)e).getQualifiedName(); }).list(); assertTrue(result.contains(clientClass.getQualifiedName())); assertTrue(result.contains(childClass.getQualifiedName())); assertFalse(result.contains(superClass.getQualifiedName())); assertFalse(result.contains(Object.class.getName())); //contract: if listener skips CHIDLREN, then skipped element is returned but all super classes are not returned result = clientClass.map(new SuperInheritanceHierarchyFunction().includingSelf(true).setListener(new CtScannerListener() { @Override public ScanningMode enter(CtElement element) { assertTrue(element instanceof CtTypeReference); if(superClass.getQualifiedName().equals(((CtTypeReference<?>)element).getQualifiedName())) { return ScanningMode.SKIP_CHILDREN; } return ScanningMode.NORMAL; } @Override public void exit(CtElement element) { assertTrue(element instanceof CtTypeReference); } })).map(e->{ assertTrue(e instanceof CtType); return ((CtType)e).getQualifiedName(); }).list(); assertTrue(result.contains(clientClass.getQualifiedName())); assertTrue(result.contains(childClass.getQualifiedName())); assertTrue(result.contains(superClass.getQualifiedName())); assertFalse(result.contains(Object.class.getName())); } @Test public void testSuperInheritanceHierarchyFunctionNoClasspath() { final Launcher launcher = new Launcher(); launcher.getEnvironment().setNoClasspath(true); launcher.addInputResource("src/test/resources/noclasspath/superclass/UnknownSuperClass.java"); launcher.buildModel(); final CtModel model = launcher.getModel(); CtClass<?> classUSC = launcher.getFactory().Class().get("UnknownSuperClass"); //contract: super inheritance scanner returns only Types on class path including final Object List<CtType> types = classUSC.map(new SuperInheritanceHierarchyFunction().includingSelf(true)).list(); assertEquals(2, types.size()); assertEquals("UnknownSuperClass", types.get(0).getQualifiedName()); assertEquals("java.lang.Object", types.get(1).getQualifiedName()); //contract: super inheritance scanner in reference mode returns type references including these which are not on class path and including final Object List<CtTypeReference> typeRefs = classUSC.map(new SuperInheritanceHierarchyFunction().includingSelf(true).returnTypeReferences(true)).list(); assertEquals(3, typeRefs.size()); assertEquals("UnknownSuperClass", typeRefs.get(0).getQualifiedName()); assertEquals("NotInClasspath", typeRefs.get(1).getQualifiedName()); assertEquals("java.lang.Object", typeRefs.get(2).getQualifiedName()); //contract: super inheritance scanner in reference mode, which starts on class which is not available in model returns no Object, because it does not know if type is class or interface typeRefs = classUSC.getSuperclass().map(new SuperInheritanceHierarchyFunction().includingSelf(true).returnTypeReferences(true)).list(); assertEquals(1, typeRefs.size()); assertEquals("NotInClasspath", typeRefs.get(0).getQualifiedName()); //contract: super inheritance scanner in type mode, which starts on class which is not available in model returns nothing types = classUSC.getSuperclass().map(new SuperInheritanceHierarchyFunction().includingSelf(true)).list(); assertEquals(0, types.size()); } }