Skip to content

Commit

Permalink
test SubstitutionVisitor
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Jun 11, 2017
1 parent b8752fe commit 3f3c20c
Show file tree
Hide file tree
Showing 10 changed files with 327 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package spoon.test.template;

import static org.junit.Assert.assertEquals;

import org.junit.Test;

import spoon.Launcher;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtStatement;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.factory.Factory;
import spoon.support.compiler.FileSystemFile;
import spoon.test.template.testclasses.InvocationSubstitutionByExpressionTemplate;
import spoon.test.template.testclasses.InvocationSubstitutionByStatementTemplate;

public class TemplateInvocationSubstitutionTest {

@Test
public void testInvocationSubstitutionByStatement() throws Exception {
//contract: the template engine supports substitution of any method invocation
Launcher spoon = new Launcher();
spoon.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/testclasses/InvocationSubstitutionByStatementTemplate.java"));

spoon.buildModel();
Factory factory = spoon.getFactory();

CtBlock<?> model = factory.Class().get(InvocationSubstitutionByStatementTemplate.class).getMethod("sample").getBody();

CtClass<?> resultKlass = factory.Class().create("Result");
CtStatement result = new InvocationSubstitutionByStatementTemplate(model).apply(resultKlass);
assertEquals("throw new java.lang.RuntimeException(\"Failed\")", result.toString());
}

@Test
public void testInvocationSubstitutionByExpression() throws Exception {
//contract: the template engine supports substitution of any method invocation
Launcher spoon = new Launcher();
spoon.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/testclasses/InvocationSubstitutionByExpressionTemplate.java"));

spoon.buildModel();
Factory factory = spoon.getFactory();

CtClass<?> resultKlass = factory.Class().create("Result");
CtBlock<?> result = new InvocationSubstitutionByExpressionTemplate(factory.createLiteral("abc")).apply(resultKlass);
assertEquals("java.lang.System.out.println(\"abc\".substring(1))", result.getStatement(0).toString());
assertEquals("java.lang.System.out.println(\"abc\".substring(1))", result.getStatement(1).toString());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void testReturnReplaceTemplate() throws Exception {

CtClass<?> resultKlass = factory.Class().create(factory.Package().getOrCreate("spoon.test.template"), "ReturnReplaceResult");
new ReturnReplaceTemplate(model).apply(resultKlass);
assertEquals("{ { if (((java.lang.System.currentTimeMillis()) % 2L) == 0) { return \"Panna\"; }else { return \"Orel\"; } }}", resultKlass.getMethod("method").getBody().toString().replaceAll("[\\r\\n\\t]+", "").replaceAll("\\s{2,}", " "));
assertEquals("{ if (((java.lang.System.currentTimeMillis()) % 2L) == 0) { return \"Panna\"; }else { return \"Orel\"; }}", resultKlass.getMethod("method").getBody().toString().replaceAll("[\\r\\n\\t]+", "").replaceAll("\\s{2,}", " "));
launcher.setSourceOutputDirectory(new File("./target/spooned/"));
launcher.getModelBuilder().generateProcessedSourceFiles(OutputType.CLASSES);
ModelUtils.canBeBuilt(new File("./target/spooned/spoon/test/template/ReturnReplaceResult.java"), 8);
Expand Down
124 changes: 120 additions & 4 deletions src/test/java/spoon/test/template/TemplateTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,29 @@
import spoon.reflect.code.CtTry;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtEnum;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtPackage;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtType;
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.ModelConsistencyChecker;
import spoon.reflect.visitor.filter.NameFilter;
import spoon.support.compiler.FileSystemFile;
import spoon.support.compiler.FileSystemFolder;
import spoon.support.template.Parameters;
import spoon.support.template.SubstitutionVisitor;
import spoon.template.Substitution;
import spoon.template.TemplateMatcher;
import spoon.template.TemplateParameter;
import spoon.test.template.testclasses.ArrayAccessTemplate;
import spoon.test.template.testclasses.InvocationTemplate;
import spoon.test.template.testclasses.LoggerModel;
import spoon.test.template.testclasses.NtonCodeTemplate;
import spoon.test.template.testclasses.SecurityCheckerTemplate;
import spoon.test.template.testclasses.SimpleTemplate;
import spoon.test.template.testclasses.SubstituteRootTemplate;
import spoon.test.template.testclasses.bounds.CheckBound;
import spoon.test.template.testclasses.bounds.CheckBoundMatcher;
import spoon.test.template.testclasses.bounds.CheckBoundTemplate;
Expand All @@ -40,18 +48,26 @@
import spoon.test.template.testclasses.inheritance.SuperTemplate;
import spoon.test.template.testclasses.logger.Logger;
import spoon.test.template.testclasses.logger.LoggerTemplateProcessor;
import spoon.test.template.testclasses.types.AClassModel;
import spoon.test.template.testclasses.types.AnEnumModel;
import spoon.test.template.testclasses.types.AnIfaceModel;

import java.io.File;
import java.io.Serializable;
import java.rmi.Remote;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
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.assertThat;
import static org.junit.Assert.fail;
Expand Down Expand Up @@ -130,10 +146,9 @@ public void testTemplateInheritance() throws Exception {
// contract: invocations are replaced by actual invocations
assertEquals("toBeOverriden()", methodWithTemplatedParameters.getBody().getStatement(3).toString());

// contract: foreach are inlined
CtBlock templatedForEach = methodWithTemplatedParameters.getBody().getStatement(4);
assertEquals("java.lang.System.out.println(0)", templatedForEach.getStatement(0).toString());
assertEquals("java.lang.System.out.println(1)", templatedForEach.getStatement(1).toString());
// contract: foreach are inlined without extra block
assertEquals("java.lang.System.out.println(0)", methodWithTemplatedParameters.getBody().getStatement(4).toString());
assertEquals("java.lang.System.out.println(1)", methodWithTemplatedParameters.getBody().getStatement(5).toString());
}

@Test
Expand Down Expand Up @@ -362,6 +377,39 @@ public void testExtensionBlock() throws Exception {
assertTrue(aTry.getBody().getStatements().size() > 1);
}

@Test
public void testExtensionDecoupledSubstitutionVisitor() throws Exception {
//contract: substitution can be done on model, which is not based on Template
final Launcher launcher = new Launcher();
launcher.setArgs(new String[] {"--output-type", "nooutput" });
launcher.addInputResource("./src/test/java/spoon/test/template/testclasses/logger/Logger.java");
launcher.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/testclasses/LoggerModel.java"));

launcher.buildModel();
Factory factory = launcher.getFactory();

final CtClass<?> aTemplateModelType = launcher.getFactory().Class().get(LoggerModel.class);
final CtMethod<?> aTemplateModel = aTemplateModelType.getMethod("block");
final CtClass<?> aTargetType = launcher.getFactory().Class().get(Logger.class);
final CtMethod<?> toBeLoggedMethod = aTargetType.getMethodsByName("enter").get(0);


Map<String, Object> params = new HashMap<>();
params.put("_classname_", aTargetType.getSimpleName()) ;
params.put("_methodName_", toBeLoggedMethod.getSimpleName());
params.put("_block_", toBeLoggedMethod.getBody());
final List<CtMethod<?>> aMethods = new SubstitutionVisitor(factory, params).substitute(aTemplateModel.clone());
assertEquals(1, aMethods.size());
final CtMethod<?> aMethod = aMethods.get(0);
assertTrue(aMethod.getBody().getStatement(0) instanceof CtTry);
final CtTry aTry = (CtTry) aMethod.getBody().getStatement(0);
assertTrue(aTry.getFinalizer().getStatement(0) instanceof CtInvocation);
assertEquals("spoon.test.template.testclasses.logger.Logger.exit(\"enter\")", aTry.getFinalizer().getStatement(0).toString());
assertTrue(aTry.getBody().getStatement(0) instanceof CtInvocation);
assertEquals("spoon.test.template.testclasses.logger.Logger.enter(\"Logger\", \"enter\")", aTry.getBody().getStatement(0).toString());
assertTrue(aTry.getBody().getStatements().size() > 1);
}

@Test
public void testTemplateInterfaces() throws Exception {
Launcher spoon = new Launcher();
Expand Down Expand Up @@ -540,4 +588,72 @@ public void testTemplateArrayAccess() throws Exception {
CtMethod<?> m2 = resultKlass.getMethod("method2");
assertEquals("java.lang.System.out.println(\"second\")", m2.getBody().getStatement(0).toString());
}

@Test
public void testStatementTemplateRootSubstitution() throws Exception {
//contract: the template engine supports substitution of root element
Launcher spoon = new Launcher();
spoon.addTemplateResource(new FileSystemFile("./src/test/java/spoon/test/template/testclasses/SubstituteRootTemplate.java"));

spoon.buildModel();
Factory factory = spoon.getFactory();

CtClass<?> templateClass = factory.Class().get(SubstituteRootTemplate.class);
CtBlock<Void> templateParam = (CtBlock) templateClass.getMethod("sampleBlock").getBody();

CtClass<?> resultKlass = factory.Class().create("Result");
CtStatement result = new SubstituteRootTemplate(templateParam).apply(resultKlass);
assertEquals("java.lang.String s = \"Spoon is cool!\"", ((CtBlock)result).getStatement(0).toString());
}

@Test
public void testInsertType() throws Exception {
final Launcher launcher = new Launcher();
launcher.setArgs(new String[] {"--output-type", "nooutput" });
launcher.addTemplateResource(new FileSystemFolder("./src/test/java/spoon/test/template/testclasses/types"));

launcher.buildModel();
Factory factory = launcher.getFactory();

CtPackage targetPackage = factory.Package().create(factory.getModel().getRootPackage(), "generated");
factory.getModel().getRootPackage().addPackage(targetPackage);


Map<String, Object> parameters = new HashMap<>();
//replace someMethod with genMethod
parameters.put("someMethod", "genMethod");

//contract: we can generate interface
final CtType<?> aIfaceModel = launcher.getFactory().Interface().get(AnIfaceModel.class);
CtType<?> genIface = Substitution.insertType(targetPackage, "GenIface", aIfaceModel, parameters);
assertNotNull(genIface);
assertSame(genIface, factory.Type().get("generated.GenIface"));
CtMethod<?> generatedIfaceMethod = genIface.getMethod("genMethod");
assertNotNull(generatedIfaceMethod);
assertNull(genIface.getMethod("someMethod"));

//add new substitution request - replace AnIfaceModel by GenIface
parameters.put("AnIfaceModel", genIface.getReference());
//contract: we can generate class
final CtType<?> aClassModel = launcher.getFactory().Class().get(AClassModel.class);
CtType<?> genClass = Substitution.insertType(targetPackage, "GenClass", aClassModel, parameters);
assertNotNull(genClass);
assertSame(genClass, factory.Type().get("generated.GenClass"));
CtMethod<?> generatedClassMethod = genClass.getMethod("genMethod");
assertNotNull(generatedClassMethod);
assertNull(genClass.getMethod("someMethod"));
assertTrue(generatedIfaceMethod!=generatedClassMethod);
assertTrue(generatedClassMethod.isOverriding(generatedIfaceMethod));

//contract: we can generate enum
parameters.put("case1", "GOOD");
parameters.put("case2", "BETTER");
final CtType<?> aEnumModel = launcher.getFactory().Type().get(AnEnumModel.class);
CtEnum<?> genEnum = (CtEnum<?>) Substitution.insertType(targetPackage, "GenEnum", aEnumModel, parameters);
assertNotNull(genEnum);
assertSame(genEnum, factory.Type().get("generated.GenEnum"));
assertEquals(2, genEnum.getEnumValues().size());
assertEquals("GOOD", genEnum.getEnumValues().get(0).getSimpleName());
assertEquals("BETTER", genEnum.getEnumValues().get(1).getSimpleName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package spoon.test.template.testclasses;

import spoon.reflect.code.CtExpression;
import spoon.template.BlockTemplate;
import spoon.template.Local;
import spoon.template.Parameter;

public class InvocationSubstitutionByExpressionTemplate extends BlockTemplate {

@Override
public void block() throws Throwable {
System.out.println(_expression_().substring(1));
System.out.println(_expression_.S().substring(1));
}

@Parameter
CtExpression<String> _expression_;

@Local
public InvocationSubstitutionByExpressionTemplate(CtExpression<String> expr) {
this._expression_ = expr;
}

@Local
String _expression_() {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package spoon.test.template.testclasses;

import spoon.reflect.code.CtStatement;
import spoon.template.Local;
import spoon.template.Parameter;
import spoon.template.StatementTemplate;

public class InvocationSubstitutionByStatementTemplate extends StatementTemplate {

@Override
public void statement() throws Throwable {
_statement_();
}

@Parameter("_statement_")
CtStatement statement;

@Local
public InvocationSubstitutionByStatementTemplate(CtStatement statement) {
this.statement = statement;
}

@Local
void _statement_() {
}

@Local
void sample() {
throw new RuntimeException("Failed");
}
}
36 changes: 36 additions & 0 deletions src/test/java/spoon/test/template/testclasses/LoggerModel.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2006-2015 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/

package spoon.test.template.testclasses;

import spoon.reflect.code.CtBlock;
import spoon.test.template.testclasses.logger.Logger;

public class LoggerModel {
private String _classname_;
private String _methodName_;
private CtBlock<?> _block_;

public void block() throws Throwable {
try {
Logger.enter(_classname_, _methodName_);
_block_.S();
} finally {
Logger.exit(_methodName_);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package spoon.test.template.testclasses;

import spoon.reflect.code.CtBlock;
import spoon.template.Local;
import spoon.template.Parameter;
import spoon.template.StatementTemplate;
import spoon.template.TemplateParameter;

public class SubstituteRootTemplate extends StatementTemplate {

@Override
public void statement() throws Throwable {
block.S();
}

@Parameter
TemplateParameter<Void> block;

@Local
public SubstituteRootTemplate(CtBlock<Void> block) {
this.block = block;
}

@Local
void sampleBlock() {
String s="Spoon is cool!";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package spoon.test.template.testclasses.types;

import java.util.AbstractList;

public class AClassModel<E> extends AbstractList<E> implements AnIfaceModel {

public AClassModel() {
}

@Override
public E get(int index) {
throw new IndexOutOfBoundsException();
}

@Override
public int size() {
return 0;
}

@Override
public void someMethod() {
}
}
Loading

0 comments on commit 3f3c20c

Please sign in to comment.