package spoon.test.lambda; import org.junit.Before; import org.junit.Test; import spoon.Launcher; import spoon.OutputType; import spoon.SpoonModelBuilder; import spoon.reflect.code.CtFieldAccess; import spoon.reflect.code.CtIf; import spoon.reflect.code.CtLambda; import spoon.reflect.code.CtTypeAccess; import spoon.reflect.declaration.CtMethod; import spoon.reflect.declaration.CtParameter; import spoon.reflect.declaration.CtType; import spoon.reflect.factory.Factory; import spoon.reflect.reference.CtArrayTypeReference; import spoon.reflect.reference.CtParameterReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.Filter; import spoon.reflect.visitor.filter.AbstractFilter; import spoon.reflect.visitor.filter.NameFilter; import spoon.reflect.visitor.filter.TypeFilter; import spoon.test.lambda.testclasses.Bar; import spoon.test.lambda.testclasses.Foo; import spoon.test.lambda.testclasses.Kuu; import spoon.test.lambda.testclasses.LambdaRxJava; import spoon.test.lambda.testclasses.Panini; import spoon.test.lambda.testclasses.Tacos; import spoon.testing.utils.ModelUtils; import java.io.File; import java.util.List; import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; 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.assertTrue; import static org.junit.Assert.fail; import static spoon.testing.utils.ModelUtils.canBeBuilt; public class LambdaTest { private Launcher launcher; private Factory factory; private CtType<Foo> foo; private CtType<Bar> bar; private CtType<Object> panini; private CtType<Object> tacos; private CtType<LambdaRxJava> lambdaRxJava; private SpoonModelBuilder compiler; @Before public void setUp() throws Exception { launcher = new Launcher(); launcher.setArgs(new String[] {"--output-type", "nooutput" }); this.factory = launcher.getFactory(); factory.getEnvironment().setComplianceLevel(8); compiler = launcher.createCompiler(this.factory); compiler.addInputSource(new File("./src/test/java/spoon/test/lambda/testclasses/")); compiler.build(); foo = factory.Type().get(Foo.class); bar = factory.Type().get(Bar.class); panini = factory.Type().get(Panini.class); tacos = factory.Type().get(Tacos.class); lambdaRxJava = factory.Type().get(LambdaRxJava.class); } @Test public void testLambdaExpressionWithExpressionBodyAndWithoutParameter() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(0); assertTypedBy(Foo.Check.class, lambda.getType()); assertParametersSizeIs(0, lambda.getParameters()); assertHasExpressionBody(lambda); assertIsWellPrinted("((spoon.test.lambda.testclasses.Foo.Check) (() -> false))", lambda); } @Test public void testTypeAccessInLambdaNoClassPath() { final Launcher runLaunch = new Launcher(); runLaunch.getEnvironment().setNoClasspath(true); runLaunch.addInputResource("./src/test/resources/noclasspath/lambdas/TypeAccessInLambda.java"); runLaunch.buildModel(); assertEquals("The token 'Strings' has not been parsed as CtTypeAccess", 1, runLaunch.getModel().getElements(new Filter<CtTypeAccess>() { @Override public boolean matches(final CtTypeAccess element) { return element.getAccessedType().getSimpleName().equals("Strings"); } }).size()); } @Test public void testFieldAccessInLambdaNoClassPath() { final Launcher runLaunch = new Launcher(); runLaunch.getEnvironment().setNoClasspath(true); runLaunch.addInputResource("./src/test/resources/noclasspath/lambdas/FieldAccessInLambda.java"); runLaunch.addInputResource("./src/test/resources/noclasspath/lambdas/imported/SeparateInterfaceWithField.java"); runLaunch.buildModel(); final List<CtFieldAccess> fieldAccesses = runLaunch.getModel().getElements(new Filter<CtFieldAccess>() { @Override public boolean matches(final CtFieldAccess element) { final String name = element.getVariable().getSimpleName(); return name.equals("localField") || name.equals("pathSeparator") || name.equals("fieldInSeparateInterface") || name.equals("fieldInClassBase") || name.equals("fieldInClass") || name.equals("fieldInInterfaceBase") || name.equals("fieldInInterface") || name.equals("iAmToLazyForAnotherFieldName"); } }); assertEquals(8, fieldAccesses.size()); } @Test public void testFieldAccessInLambdaNoClassPathExternal1Example() { final Launcher runLaunch = new Launcher(); runLaunch.getEnvironment().setNoClasspath(true); runLaunch.addInputResource("./src/test/resources/noclasspath/lambdas/external1"); runLaunch.buildModel(); assertEquals(3, runLaunch.getModel().getElements(new Filter<CtFieldAccess>() { @Override public boolean matches(final CtFieldAccess element) { return element.getVariable().getSimpleName().equals("DEFAULT_RATING"); } }).size()); } @Test public void testLambdaExpressionWithExpressionBodyAndWithoutTypeForParameter() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(1); assertTypedBy(Predicate.class, lambda.getType()); assertParametersSizeIs(1, lambda.getParameters()); final CtParameter<?> parameter = (CtParameter<?>) lambda.getParameters().get(0); assertParameterTypedBy(Foo.Person.class, parameter); assertParameterIsNamedBy("p", parameter); assertHasExpressionBody(lambda); assertIsWellPrinted( "((java.util.function.Predicate<spoon.test.lambda.testclasses.Foo.Person>) (( p) -> (p.age) > 10))", lambda); } @Test public void testLambdaExpressionWithExpressionBodyAndWithMultiParameters() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(2); assertTypedBy(Foo.CheckPersons.class, lambda.getType()); assertParametersSizeIs(2, lambda.getParameters()); final CtParameter<?> parameter1 = (CtParameter<?>) lambda.getParameters().get(0); assertParameterTypedBy(Foo.Person.class, parameter1); assertParameterIsNamedBy("p1", parameter1); final CtParameter<?> parameter2 = (CtParameter<?>) lambda.getParameters().get(1); assertParameterTypedBy(Foo.Person.class, parameter2); assertParameterIsNamedBy("p2", parameter2); assertHasExpressionBody(lambda); assertIsWellPrinted( "((spoon.test.lambda.testclasses.Foo.CheckPersons) (( p1, p2) -> ((p1.age) - (p2.age)) > 0))", lambda); } @Test public void testLambdaExpressionWithExpressionBodyAndWithParameterTyped() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(3); assertTypedBy(Predicate.class, lambda.getType()); assertParametersSizeIs(1, lambda.getParameters()); final CtParameter<?> parameter = (CtParameter<?>) lambda.getParameters().get(0); assertParameterTypedBy(Foo.Person.class, parameter); assertParameterIsNamedBy("p", parameter); assertHasExpressionBody(lambda); assertIsWellPrinted( "((java.util.function.Predicate<spoon.test.lambda.testclasses.Foo.Person>) ((spoon.test.lambda.testclasses.Foo.Person p) -> (p.age) > 10))", lambda); } @Test public void testLambdaExpressionWithExpressionBodyAndWithMultiParametersTyped() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(4); assertTypedBy(Foo.CheckPersons.class, lambda.getType()); assertParametersSizeIs(2, lambda.getParameters()); final CtParameter<?> parameter1 = (CtParameter<?>) lambda.getParameters().get(0); assertParameterTypedBy(Foo.Person.class, parameter1); assertParameterIsNamedBy("p1", parameter1); final CtParameter<?> parameter2 = (CtParameter<?>) lambda.getParameters().get(1); assertParameterTypedBy(Foo.Person.class, parameter2); assertParameterIsNamedBy("p2", parameter2); assertHasExpressionBody(lambda); assertIsWellPrinted( "((spoon.test.lambda.testclasses.Foo.CheckPersons) ((spoon.test.lambda.testclasses.Foo.Person p1,spoon.test.lambda.testclasses.Foo.Person p2) -> ((p1.age) - (p2.age)) > 0))", lambda); } @Test public void testLambdaExpressionWithStatementBodyAndWithoutParameters() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(5); assertTypedBy(Foo.Check.class, lambda.getType()); assertParametersSizeIs(0, lambda.getParameters()); assertStatementBody(lambda); assertIsWellPrinted("((spoon.test.lambda.testclasses.Foo.Check) (() -> {" + System.lineSeparator() + " java.lang.System.err.println(\"\");" + System.lineSeparator() + " return false;" + System.lineSeparator() + "}))", lambda); } @Test public void testLambdaExpressionWithStatementBodyAndWithParameter() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(6); assertTypedBy(Predicate.class, lambda.getType()); assertParametersSizeIs(1, lambda.getParameters()); final CtParameter<?> parameter = (CtParameter<?>) lambda.getParameters().get(0); assertParameterTypedBy(Foo.Person.class, parameter); assertParameterIsNamedBy("p", parameter); assertStatementBody(lambda); assertIsWellPrinted( "((java.util.function.Predicate<spoon.test.lambda.testclasses.Foo.Person>) (( p) -> {" + System.lineSeparator() + " p.doSomething();" + System.lineSeparator() + " return (p.age) > 10;" + System.lineSeparator() + "}))", lambda); } @Test public void testLambdaExpressionInIfConditional() throws Exception { final CtLambda<?> lambda = getLambdaInFooByNumber(7); assertTypedBy(Predicate.class, lambda.getType()); assertParametersSizeIs(1, lambda.getParameters()); final CtParameter<?> parameter = (CtParameter<?>) lambda.getParameters().get(0); assertParameterTypedBy(Foo.Person.class, parameter); assertParameterIsNamedBy("p", parameter); assertHasExpressionBody(lambda); final CtMethod<?> method = foo.getElements(new NameFilter<CtMethod<?>>("m8")).get(0); final CtIf condition = method.getElements(new AbstractFilter<CtIf>(CtIf.class) { @Override public boolean matches(CtIf element) { return true; } }).get(0); final String expected = "if (((java.util.function.Predicate<spoon.test.lambda.testclasses.Foo.Person>) (( p) -> (p.age) > 18)).test(new spoon.test.lambda.testclasses.Foo.Person(10))) {" + System.lineSeparator() + " java.lang.System.err.println(\"Enjoy, you have more than 18.\");" + System .lineSeparator() + "}"; assertEquals("Condition must be well printed", expected, condition.toString()); } @Test public void testCompileLambdaGeneratedBySpoon() throws Exception { launcher.setSourceOutputDirectory(new File("./target/spooned/")); launcher.getModelBuilder().generateProcessedSourceFiles(OutputType.CLASSES); canBeBuilt(new File("./target/spooned/spoon/test/lambda/testclasses/"), 8); } @Test public void testTypeParameterOfLambdaWithoutType() throws Exception { final CtLambda<?> lambda1 = bar.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class)).get(0); assertEquals(1, lambda1.getParameters().size()); final CtParameter<?> ctParameterFirstLambda = lambda1.getParameters().get(0); assertEquals("s", ctParameterFirstLambda.getSimpleName()); assertTrue(ctParameterFirstLambda.getType().isImplicit()); assertEquals("", ctParameterFirstLambda.getType().toString()); assertEquals("SingleSubscriber", ctParameterFirstLambda.getType().getSimpleName()); } @Test public void testTypeParameterOfLambdaWithoutType2() throws Exception { final CtLambda<?> lambda2 = bar.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class)).get(1); assertEquals(2, lambda2.getParameters().size()); final CtParameter<?> ctParameterSecondLambda = lambda2.getParameters().get(0); assertEquals("v", ctParameterSecondLambda.getSimpleName()); assertTrue(ctParameterSecondLambda.getType().isImplicit()); assertEquals("", ctParameterSecondLambda.getType().toString()); assertEquals("?", ctParameterSecondLambda.getType().getSimpleName()); } @Test public void testTypeParameterWithImplicitArrayType() throws Exception { final CtLambda<?> lambda = panini.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class)).get(0); assertEquals(1, lambda.getParameters().size()); final CtParameter<?> ctParameter = lambda.getParameters().get(0); assertEquals("a", ctParameter.getSimpleName()); assertTrue(ctParameter.getType().isImplicit()); assertEquals("", ctParameter.getType().toString()); assertEquals("Object[]", ctParameter.getType().getSimpleName()); final CtArrayTypeReference typeParameter = (CtArrayTypeReference) ctParameter.getType(); assertTrue(typeParameter.getComponentType().isImplicit()); assertEquals("", typeParameter.getComponentType().toString()); assertEquals("Object", typeParameter.getComponentType().getSimpleName()); } @Test public void testLambdaWithPrimitiveParameter() throws Exception { final CtLambda<?> lambda = tacos.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class)).get(0); assertEquals(2, lambda.getParameters().size()); final CtParameter<?> firstParam = lambda.getParameters().get(0); assertEquals("rs", firstParam.getSimpleName()); assertTrue(firstParam.getType().isImplicit()); assertEquals("", firstParam.getType().toString()); assertEquals("ResultSet", firstParam.getType().getSimpleName()); final CtParameter<?> secondParam = lambda.getParameters().get(1); assertEquals("i", secondParam.getSimpleName()); assertTrue(secondParam.getType().isImplicit()); assertEquals("", secondParam.getType().toString()); assertEquals("int", secondParam.getType().getSimpleName()); } @Test public void testBuildExecutableReferenceFromLambda() throws Exception { final CtType<Kuu> aType = ModelUtils.buildClass(Kuu.class); final CtLambda<?> aLambda = aType.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class)).get(0); List<? extends CtParameterReference<?>> collect = null; try { collect = aLambda.getParameters().stream().map(CtParameter::getReference).collect(Collectors.toList()); } catch (ClassCastException e) { fail(); } assertNotNull(collect); assertEquals(1, collect.size()); } @Test public void testEqualsLambdaParameterRef() throws Exception { CtLambda<?> lambda = getLambdaInFooByNumber(8); CtParameter<?> param = (CtParameter<?>)lambda.getParameters().get(0); CtParameterReference paramRef1 = param.getReference(); CtParameterReference paramRef2 = lambda.filterChildren(new TypeFilter<>(CtParameterReference.class)).first(); assertTrue(paramRef1.getDeclaringExecutable().getType().equals(paramRef2.getDeclaringExecutable().getType())); assertTrue(paramRef1.equals(paramRef2)); } @Test public void testLambdaMethod() throws Exception { CtLambda<?> lambda = getLambdaInFooByNumber(8); CtMethod<?> method = lambda.getOverriddenMethod(); CtTypeReference<?> iface = lambda.getType(); assertEquals(Consumer.class.getName(), iface.getQualifiedName()); assertEquals(iface.getTypeDeclaration().getMethodsByName("accept").get(0), method); /* This assertion fails now CtExecutableReference<?> lambdaRef = lambda.getReference(); CtExecutableReference<?> methodRef = lambdaRef.getOverridingExecutable(); // because methodRef is null CtExecutable<?> method2 = methodRef.getDeclaration(); assertEquals("The lambda.getMethod() != lambda.getReference().getOverridingExecutable().getDeclaration()", method, method2); */ } @Test public void testGetOverriddenMethodWithFunction() throws Exception { List<CtLambda<?>> allLambdas = lambdaRxJava.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class)); assertEquals(1, allLambdas.size()); CtLambda<?> lambda = allLambdas.get(0); CtMethod<?> method = lambda.getOverriddenMethod(); CtTypeReference<?> iface = lambda.getType(); assertEquals(LambdaRxJava.NbpOperator.class.getName(), iface.getQualifiedName()); } private void assertTypedBy(Class<?> expectedType, CtTypeReference<?> type) { assertEquals("Lambda must be typed", expectedType, type.getActualClass()); } private void assertParametersSizeIs(int nbParameters, List<CtParameter<?>> parameters) { if (nbParameters == 0) { assertEquals("Lambda hasn't parameters", nbParameters, parameters.size()); } else { assertEquals("Lambda has parameters", nbParameters, parameters.size()); } } private void assertParameterTypedBy(Class<?> expectedType, CtParameter<?> parameter) { assertNotNull("Lambda has a parameter typed", parameter.getType()); assertEquals("Lambda has a parameter typed by", expectedType, parameter.getType().getActualClass()); } private void assertHasExpressionBody(CtLambda<?> lambda) { assertNotNull("Lambda has an expression for its body.", lambda.getExpression()); assertNull("Lambda don't have a list of statements (body) for its body", lambda.getBody()); } private void assertStatementBody(CtLambda<?> lambda) { assertNotNull("Lambda has a body with statements.", lambda.getBody()); assertNull("Lambda don't have an expression for its body", lambda.getExpression()); } private void assertParameterIsNamedBy(String name, CtParameter<?> parameter) { assertEquals("Lambda has a parameter with a name", name, parameter.getSimpleName()); } private void assertIsWellPrinted(String expected, CtLambda<?> lambda) { assertEquals("Lambda must be well printed", expected, lambda.toString()); } // note that the lambda number in simple name depends on the classloader // Eclipse the name is one less than in Maven // hence w ehcnage the tests private CtLambda<?> getLambdaInFooByNumber(int number) { return foo.getElements(new TypeFilter<CtLambda<?>>(CtLambda.class)).get(number); } }