Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev friendly amp improvements and windows support #1004

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package eu.stamp_project.dspot.amplifier.amplifiers;

import eu.stamp_project.dspot.common.configuration.options.CommentEnum;
import eu.stamp_project.dspot.common.miscellaneous.CloneHelper;
import eu.stamp_project.dspot.common.miscellaneous.Counter;
import eu.stamp_project.dspot.common.miscellaneous.DSpotUtils;
import spoon.reflect.code.CtComment;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtMethod;

Expand Down Expand Up @@ -71,8 +74,12 @@ protected List<T> reduceAlreadyAmplifiedElements(List<T> elementsToBeReduced) {
protected CtMethod<?> replace(T originalElement, T amplifiedElement, CtMethod<?> testMethod) {
originalElement.replace(amplifiedElement);
amplifiedElement.putMetadata(this.METADATA_KEY, true);
CtMethod<?> clone = CloneHelper.cloneTestMethodForAmp(testMethod, getSuffix());
DSpotUtils.addComment(amplifiedElement,
getSuffix() + ": changed '" + originalElement + "' to '" + amplifiedElement + "'",
CtComment.CommentType.INLINE, CommentEnum.Amplifier);
CtMethod<?> clone = CloneHelper.cloneTestMethodForAmp(testMethod, "_" + getSuffix());
amplifiedElement.replace(originalElement);
DSpotUtils.removeComments(originalElement, getSuffix());
Counter.updateInputOf(clone, 1);
return clone;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
package eu.stamp_project.dspot.amplifier.amplifiers;

import eu.stamp_project.dspot.common.configuration.options.CommentEnum;
import eu.stamp_project.dspot.common.miscellaneous.DSpotUtils;
import eu.stamp_project.dspot.common.test_framework.TestFramework;
import eu.stamp_project.dspot.common.miscellaneous.AmplificationHelper;
import eu.stamp_project.dspot.common.miscellaneous.CloneHelper;
import eu.stamp_project.dspot.common.miscellaneous.Counter;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtStatementList;
import spoon.reflect.code.CtTry;
import spoon.reflect.code.CtWhile;
import spoon.reflect.code.*;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.visitor.filter.TypeFilter;
Expand Down Expand Up @@ -54,6 +51,8 @@ private CtMethod<?> apply(CtMethod<?> method, CtInvocation<?> invocation) {
ctStatementList.getStatements().get(indexOfInvocation).insertAfter(invocation);
}
Counter.updateInputOf(cloned, 1);
DSpotUtils.addComment(ctStatementList,"MethodCallRemover: removed call '" + invocation + "'",
CtComment.CommentType.INLINE, CommentEnum.Amplifier);
return cloned;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,36 +106,37 @@ public TestTuple removeAssertions(CtType<?> testClass, List<CtMethod<?>> tests)
CtType<?> cloneClass = testClass.clone();
testClass.getPackage().addType(cloneClass);
if (devFriendlyAmplification) {
return new TestTuple(cloneClass, removeAssertionsAndTrailingInvocations(tests,cloneClass));
return new TestTuple(cloneClass, removeAssertionsCompletely(tests,cloneClass));
} else {
return new TestTuple(cloneClass, removeAssertions(tests, cloneClass));
}
}

/**
* Uses {@link AssertionRemover#removeAssertion(CtMethod)} to remove existing assertions from cloned test methods
* Uses {@link AssertionRemover#removeAssertions(CtMethod, boolean)} to remove existing assertions from cloned
* test methods, but leaves the arguments of the assertions
* @param tests
* @param cloneClass
* @return
*/
private List<CtMethod<?>> removeAssertions(List<CtMethod<?>> tests, CtType<?> cloneClass){
List<CtMethod<?>> testsWithoutAssertions = tests.stream()
.map(this.assertionRemover::removeAssertion)
.map(test -> this.assertionRemover.removeAssertions(test, true))
.collect(Collectors.toList());
testsWithoutAssertions.forEach(cloneClass::addMethod);
return testsWithoutAssertions;
}

/**
* Uses {@link AssertionRemover#removeAssertion(CtMethod)} to remove existing assertions from cloned test methods
* Uses {@link AssertionRemover#removeAssertions(CtMethod, boolean)} to remove existing assertions and their
* arguments from cloned test methods
* @param tests
* @param cloneClass
* @return
*/
private List<CtMethod<?>> removeAssertionsAndTrailingInvocations(List<CtMethod<?>> tests, CtType<?> cloneClass){
private List<CtMethod<?>> removeAssertionsCompletely(List<CtMethod<?>> tests, CtType<?> cloneClass){
List<CtMethod<?>> testsWithoutAssertions = tests.stream()
.map(this.assertionRemover::removeAssertion)
.map(this.assertionRemover::removeArgumentsOfTrailingAssertions)
.map(test -> this.assertionRemover.removeAssertions(test, false))
.collect(Collectors.toList());
testsWithoutAssertions.forEach(cloneClass::addMethod);
return testsWithoutAssertions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ public Map<CtMethod<?>, List<CtLocalVariable<?>>> getVariableAssertedPerTestMeth
* @param testMethod Test method
* @return Test's clone without any assertion
*/
public CtMethod<?> removeAssertion(CtMethod<?> testMethod) {
public CtMethod<?> removeAssertions(CtMethod<?> testMethod, boolean leaveArguments) {
CtMethod<?> testWithoutAssertion = CloneHelper.cloneTestMethodNoAmp(testMethod);
variableAssertedPerTestMethod.put(testWithoutAssertion,
testWithoutAssertion
.getElements(TestFramework.ASSERTIONS_FILTER)
.stream()
.filter(invocation -> !(invocation.getParent() instanceof CtRHSReceiver)) // it means that the return type is used in the test.
.flatMap(invocation -> this.removeAssertion(invocation).stream())
.flatMap(invocation -> this.removeAssertion(invocation, leaveArguments).stream())
.collect(Collectors.toList())
);

Expand All @@ -64,34 +64,13 @@ public boolean matches(CtTry element) {
}

/**
* Can be called after {@link AssertionRemover#removeAssertion(CtMethod)} to remove the statements that
* previously were arguments of assertions all the way at
* @param testMethod
* @return
*/
public CtMethod<?> removeArgumentsOfTrailingAssertions(CtMethod<?> testMethod) {
List<CtStatement> testStatements = testMethod.getElements(new TypeFilter<>(CtStatement.class));

for (int i = testStatements.size() - 1; i >= 0; i--) {
CtStatement statement = testStatements.get(i);
Object metadata = statement.getMetadata(AssertionGeneratorUtils.METADATA_WAS_IN_ASSERTION);
if (metadata != null && (Boolean) metadata) {
testMethod.getBody().removeStatement(statement);
} else {
break;
}
}

return testMethod;
}

/**
* Replaces an invocation with its arguments.
* Remove an invocation and optionally replace with its arguments
*
* @param invocation Invocation
* @param leaveArguments if true: replace the invocation with its arguments
* @return the list of local variables extracted from assertions
*/
public List<CtLocalVariable<?>> removeAssertion(CtInvocation<?> invocation) {
public List<CtLocalVariable<?>> removeAssertion(CtInvocation<?> invocation, boolean leaveArguments) {
List<CtLocalVariable<?>> variableReadsAsserted = new ArrayList<>();
final Factory factory = invocation.getFactory();
final TypeFilter<CtStatement> statementTypeFilter = new TypeFilter<CtStatement>(CtStatement.class) {
Expand All @@ -108,42 +87,40 @@ public boolean matches(CtStatement element) {
if (clone instanceof CtUnaryOperator) {
clone = ((CtUnaryOperator) clone).getOperand();
}
if (clone instanceof CtLambda) {
CtLambda lambda = ((CtLambda) clone);
if (lambda.getBody() != null) {
invocation.getParent(CtStatementList.class).insertBefore(
statementTypeFilter,
factory.createStatementList(lambda.getBody())
);
} else {
// in case of we have something like () -> "string"
if (lambda.getExpression() instanceof CtLiteral) {
continue;
if (leaveArguments) {
if (clone instanceof CtLambda) {
CtLambda lambda = ((CtLambda) clone);
if (lambda.getBody() != null) {
invocation.getParent(CtStatementList.class)
.insertBefore(statementTypeFilter, factory.createStatementList(lambda.getBody()));
} else {
// in case of we have something like () -> "string"
if (lambda.getExpression() instanceof CtLiteral) {
continue;
}
// TODO check that we support all cases by casting into CtInvocation
final CtBlock block = factory.createBlock();
block.setStatements(Collections.singletonList((CtInvocation) lambda.getExpression().clone()));
invocation.getParent(CtStatementList.class).insertBefore(statementTypeFilter,
factory.createStatementList(block));
}
// TODO check that we support all cases by casting into CtInvocation
final CtBlock block = factory.createBlock();
block.setStatements(Collections.singletonList((CtInvocation)lambda.getExpression().clone()));
invocation.getParent(CtStatementList.class).insertBefore(
statementTypeFilter, factory.createStatementList(block)
);
}
} else if (clone instanceof CtStatement) {
invocation.getParent(CtStatementList.class).insertBefore(statementTypeFilter, (CtStatement) clone);
clone.putMetadata(AssertionGeneratorUtils.METADATA_WAS_IN_ASSERTION, true);
} else if (!(clone instanceof CtLiteral || clone instanceof CtVariableRead)) {
// TODO EXPLAIN
CtTypeReference<?> typeOfParameter = clone.getType();
if (clone.getType() == null || factory.Type().NULL_TYPE.equals(clone.getType())) {
typeOfParameter = factory.Type().createReference(Object.class);
} else if (clone instanceof CtStatement) {
invocation.getParent(CtStatementList.class).insertBefore(statementTypeFilter,
(CtStatement) clone);
clone.putMetadata(AssertionGeneratorUtils.METADATA_WAS_IN_ASSERTION, true);
} else if (!(clone instanceof CtLiteral || clone instanceof CtVariableRead)) {
// TODO EXPLAIN
CtTypeReference<?> typeOfParameter = clone.getType();
if (clone.getType() == null || factory.Type().NULL_TYPE.equals(clone.getType())) {
typeOfParameter = factory.Type().createReference(Object.class);
}
final CtLocalVariable localVariable = factory.createLocalVariable(typeOfParameter,
toCorrectJavaIdentifier(typeOfParameter.getSimpleName()) + "_" + counter[0]++, clone);
invocation.getParent(CtStatementList.class).insertBefore(statementTypeFilter, localVariable);
localVariable.putMetadata(AssertionGeneratorUtils.METADATA_WAS_IN_ASSERTION, true);
}
final CtLocalVariable localVariable = factory.createLocalVariable(
typeOfParameter,
toCorrectJavaIdentifier(typeOfParameter.getSimpleName()) + "_" + counter[0]++,
clone
);
invocation.getParent(CtStatementList.class).insertBefore(statementTypeFilter, localVariable);
localVariable.putMetadata(AssertionGeneratorUtils.METADATA_WAS_IN_ASSERTION, true);
} else if (clone instanceof CtVariableRead && !(clone instanceof CtFieldRead)) {
}
if (clone instanceof CtVariableRead && !(clone instanceof CtFieldRead)) {
final CtVariableReference variable = ((CtVariableRead) clone).getVariable();
final List<CtLocalVariable> assertedVariables = invocation.getParent(CtBlock.class).getElements(
localVariable -> localVariable.getSimpleName().equals(variable.getSimpleName())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import eu.stamp_project.dspot.common.collector.smtp.EmailSender;
import eu.stamp_project.dspot.common.configuration.test_finder.TestFinder;
import org.apache.commons.io.FileUtils;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -150,14 +149,15 @@ public String completeDependencies(UserInput configuration, AutomaticBuilder aut
configuration.setDependencies(dependencies);
}
// TODO checks this. Since we support different Test Support, we may not need to add artificially junit in the classpath
if (!dependencies.contains("junit" + File.separator + "junit" + File.separator + "4")) {
dependencies = Test.class
.getProtectionDomain()
.getCodeSource()
.getLocation()
.getFile() +
AmplificationHelper.PATH_SEPARATOR + dependencies;
}
// if (!dependencies.contains("junit" + File.separator + "junit" + File.separator + "4")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this code is no longer useful, please remove it completely

// dependencies = Test.class
// .getProtectionDomain()
// .getCodeSource()
// .getLocation()
// .getFile() +
// AmplificationHelper.PATH_SEPARATOR + dependencies;
// System.out.println("dependencies at end of junit block: " + dependencies);
// }
if (!additionalClasspathElements.isEmpty()) {
String pathToAdditionalClasspathElements = additionalClasspathElements;
if (!Paths.get(additionalClasspathElements).isAbsolute()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public class UserInput {
"that points to the folder that contains sources (.java)." +
" Default value: ${DEFAULT-VALUE}"
)
private String pathToSourceCode = "src/main/java/";
private String pathToSourceCode = "src" + File.separator + "main" + File.separator + "java" + File.separator;

@CommandLine.Option(
names = "--relative-path-to-test-code",
Expand All @@ -71,7 +71,7 @@ public class UserInput {
"that points to the folder that contains test sources (.java)." +
" Default value: ${DEFAULT-VALUE}"
)
private String pathToTestSourceCode = "src/test/java/";
private String pathToTestSourceCode = "src" + File.separator + "test" + File.separator + "java" + File.separator;

@CommandLine.Option(
names = "--relative-path-to-classes",
Expand All @@ -80,7 +80,7 @@ public class UserInput {
"that points to the folder that contains binaries of the source (.class)." +
" Default value: ${DEFAULT-VALUE}"
)
private String pathToClasses = "target/classes/";
private String pathToClasses = "target" + File.separator + "classes" + File.separator;

@CommandLine.Option(
names = "--relative-path-to-test-classes",
Expand All @@ -89,7 +89,7 @@ public class UserInput {
"that points to the folder that contains binaries of the test source (.class)." +
" Default value: ${DEFAULT-VALUE}"
)
private String pathToTestClasses = "target/test-classes/";
private String pathToTestClasses = "target" + File.separator + "test-classes" + File.separator;

/*
Amplification process configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -197,6 +198,26 @@ public static void addComment(CtElement element, String content, CtComment.Comme
}
}
}
public static void removeComments(CtElement element, String suffixOfComment) {
if (element instanceof CtLiteral) {
try {
CtElement parentLine = element.getParent(new LineFilter());
if (parentLine != null) {
element = parentLine;
}
} catch (ParentNotInitializedException ignored) {

}
}

List<CtComment> ampComments = element.getComments().stream()
.filter(ctComment -> ctComment.getContent().startsWith(suffixOfComment))
.collect(Collectors.toList());
for (CtComment ampComment : ampComments) {
element.removeComment(ampComment);
}

}

public static final String PATH_TO_DSPOT_DEPENDENCIES = "target/dspot/dependencies/";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package eu.stamp_project.dspot.common.test_framework.implementations.junit;

import eu.stamp_project.dspot.common.configuration.options.CommentEnum;
import eu.stamp_project.dspot.common.miscellaneous.DSpotUtils;
import eu.stamp_project.testrunner.runner.Failure;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtFieldRead;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtLambda;
import spoon.reflect.code.*;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtExecutableReference;
Expand Down Expand Up @@ -72,6 +71,10 @@ public CtMethod<?> generateExpectedExceptionsBlock(CtMethod<?> test, Failure fai
);
CtBlock body = factory.Core().createBlock();
body.addStatement(invocation);
DSpotUtils.addComment(invocation,
"AssertionGenerator generate try/catch block with fail statement",
CtComment.CommentType.INLINE,
CommentEnum.Amplifier);

test.setBody(body);
test.setSimpleName(test.getSimpleName() + "_failAssert" + (numberOfFail));
Expand Down
2 changes: 1 addition & 1 deletion dspot/src/test/java/eu/stamp_project/MainTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public void testDevFriendlyAmplification() throws Exception {
Main.main(new String[]{
"--verbose",
"--absolute-path-to-project-root", new File("src/test/resources/test-projects/").getAbsolutePath() + "/",
"--amplifiers", "FastLiteralAmplifier",
"--amplifiers", "MethodAdderOnExistingObjectsAmplifier",
"--test-criterion", "ExtendedCoverageSelector",
"--test", "example.TestSuiteExample2",
"--dev-friendly",
Expand Down
Loading