Skip to content

Commit

Permalink
feat: generate log statement on asserted values in original test (#458)
Browse files Browse the repository at this point in the history
* feat: generate log statement on asserted values in original test

* fix: ArrayIndexOutBound because of an unmatched predicate. Using now simple names rather than references
  • Loading branch information
danglotb committed Jul 6, 2018
1 parent ae3ac89 commit d1ccb99
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ public List<CtMethod<?>> assertionAmplification(CtType<?> testClass, List<CtMeth
.map(this.assertionRemover::removeAssertion)
.collect(Collectors.toList());
testsWithoutAssertions.forEach(cloneClass::addMethod);
this.methodsAssertGenerator = new MethodsAssertGenerator(testClass, this.configuration, compiler);
this.methodsAssertGenerator = new MethodsAssertGenerator(
testClass,
this.configuration,
compiler,
this.assertionRemover.getVariableAssertedPerTestMethod()
);
final List<CtMethod<?>> amplifiedTestsWithAssertions =
this.innerAssertionAmplification(cloneClass, testsWithoutAssertions);
if (amplifiedTestsWithAssertions.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ static boolean isVoidReturn(CtInvocation invocation) {
);
}

static CtMethod<?> createTestWithLog(CtMethod test, final String filter) {
static CtMethod<?> createTestWithLog(CtMethod test, final String filter,
List<CtLocalVariable<?>> ctVariableReads) {
CtMethod clone = AmplificationHelper.cloneTestMethodNoAmp(test);
clone.setSimpleName(test.getSimpleName() + "_withlog");
final List<CtStatement> allStatement = clone.getElements(new TypeFilter<>(CtStatement.class));
allStatement.stream()
.filter(statement -> isStmtToLog(filter, statement))
.filter(statement -> isStmtToLog(filter, statement) || ctVariableReads.contains(statement))
.forEach(statement ->
addLogStmt(statement,
test.getSimpleName() + "__" + indexOfByRef(allStatement, statement))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package eu.stamp_project.dspot.assertGenerator;

import eu.stamp_project.utils.AmplificationHelper;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtFieldRead;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtLiteral;
import spoon.reflect.code.CtLocalVariable;
Expand All @@ -13,8 +15,16 @@
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.filter.TypeFilter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* Created by Benjamin DANGLOT
* benjamin.danglot@inria.fr
Expand All @@ -25,6 +35,12 @@ public class AssertionRemover {

private final int[] counter = new int[]{0};

private Map<CtMethod<?>, List<CtLocalVariable<?>>> variableAssertedPerTestMethod = new HashMap<>();

public Map<CtMethod<?>, List<CtLocalVariable<?>>> getVariableAssertedPerTestMethod() {
return variableAssertedPerTestMethod;
}

/**
* Removes all assertions from a test.
*
Expand All @@ -33,8 +49,13 @@ public class AssertionRemover {
*/
public CtMethod<?> removeAssertion(CtMethod<?> testMethod) {
CtMethod<?> testWithoutAssertion = AmplificationHelper.cloneTestMethodNoAmp(testMethod);
testWithoutAssertion.getElements(AmplificationHelper.ASSERTIONS_FILTER)
.forEach(this::removeAssertion);
variableAssertedPerTestMethod.put(testWithoutAssertion,
testWithoutAssertion
.getElements(AmplificationHelper.ASSERTIONS_FILTER)
.stream()
.flatMap(invocation -> this.removeAssertion(invocation).stream())
.collect(Collectors.toList())
);
return testWithoutAssertion;
}

Expand All @@ -43,7 +64,8 @@ public CtMethod<?> removeAssertion(CtMethod<?> testMethod) {
*
* @param invocation Invocation
*/
public void removeAssertion(CtInvocation<?> invocation) {
public List<CtLocalVariable<?>> removeAssertion(CtInvocation<?> invocation) {
List<CtLocalVariable<?>> variableReadsAsserted = new ArrayList<>();
final Factory factory = invocation.getFactory();
final TypeFilter<CtStatement> statementTypeFilter = new TypeFilter<CtStatement>(CtStatement.class) {
@Override
Expand All @@ -70,6 +92,18 @@ public boolean matches(CtStatement element) {
clone
);
invocation.getParent(CtStatementList.class).insertBefore(statementTypeFilter, localVariable);
} else if (clone instanceof CtVariableRead && !(clone instanceof CtFieldRead)) {
final CtVariableReference variable = ((CtVariableRead) clone).getVariable();
variableReadsAsserted.add(invocation.getParent(CtBlock.class).getElements(
(Filter<CtLocalVariable>) localVariable ->
localVariable.getSimpleName().equals(variable.getSimpleName()) // here, we match the simple name
// since the type cannot match with generated elements
// for instance, if the original element is a primitive char,
// the generated element can be a Character
// and thus, the localVariable.getReference().equals(variable) returns false
// the contract on name holds since we control it, i.e. variables in
// assertions are extracted by us.
).get(0));
}
}
// must find the first statement list to remove the invocation from it, e.g. the block that contains the assertions
Expand All @@ -79,6 +113,7 @@ public boolean matches(CtStatement element) {
topStatement = topStatement.getParent();
}
((CtStatementList) topStatement.getParent()).removeStatement((CtStatement) topStatement);
return variableReadsAsserted;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,24 @@ public class MethodsAssertGenerator {

private DSpotCompiler compiler;

public MethodsAssertGenerator(CtType originalClass, InputConfiguration configuration, DSpotCompiler compiler) {
private Map<CtMethod<?>, List<CtLocalVariable<?>>> variableReadsAsserted;

public MethodsAssertGenerator(CtType originalClass,
InputConfiguration configuration,
DSpotCompiler compiler,
Map<CtMethod<?>, List<CtLocalVariable<?>>> variableReadsAsserted) {
this.originalClass = originalClass;
this.configuration = configuration;
this.compiler = compiler;
this.factory = configuration.getFactory();
this.variableReadsAsserted = variableReadsAsserted;
}

/**
* Adds new assertions in multiple tests.
* <p>
* <p>Instruments the tests to have observation points.
* Details in {@link AssertGeneratorHelper#createTestWithLog(CtMethod, String)}.
* Details in {@link AssertGeneratorHelper#createTestWithLog(CtMethod, String, List)}.
* <p>
* <p>Details of the assertion generation in {@link #buildTestWithAssert(CtMethod, Map)}.
*
Expand All @@ -74,8 +80,10 @@ public List<CtMethod<?>> addAssertions(CtType<?> testClass, List<CtMethod<?>> te
final List<CtMethod<?>> testCasesWithLogs = testCases.stream()
.map(ctMethod -> {
DSpotUtils.printProgress(testCases.indexOf(ctMethod), testCases.size());
return AssertGeneratorHelper.createTestWithLog(ctMethod,
this.originalClass.getPackage().getQualifiedName()
return AssertGeneratorHelper.createTestWithLog(
ctMethod,
this.originalClass.getPackage().getQualifiedName(),
this.variableReadsAsserted.get(ctMethod)
);
}
).collect(Collectors.toList());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.filter.TypeFilter;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -108,8 +109,8 @@ public void testOnLoops() throws Exception {
"}",
AssertGeneratorHelper.createTestWithLog(
new AssertionRemover().removeAssertion(Utils.findMethod("fr.inria.sample.TestClassWithLoop", "test2")),
"fr.inria.sample"
).toString()
"fr.inria.sample",
Collections.emptyList()).toString()
);
}

Expand All @@ -135,7 +136,7 @@ public void testNoInstrumentationOnGeneratedObject() throws Exception {
assertEquals(5, amplifiedMethods.size());

final List<CtMethod<?>> instrumentedAmplifiedTests = amplifiedMethods.stream()
.map(method -> AssertGeneratorHelper.createTestWithLog(method, "fr.inria.statementaddarray"))
.map(method -> AssertGeneratorHelper.createTestWithLog(method, "fr.inria.statementaddarray", Collections.emptyList()))
.collect(Collectors.toList());

assertEquals(5, instrumentedAmplifiedTests.size());
Expand All @@ -158,7 +159,7 @@ public boolean matches(CtInvocation element) {
public void testMultipleObservationsPoints() throws Exception {
final CtMethod<?> test1 = Utils.findMethod("fr.inria.multipleobservations.TestClassToBeTest", "test");
final CtMethod<?> testWithLog =
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.multipleobservations");
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.multipleobservations", Collections.emptyList());
final String expectedMethodWithLogs = "@org.junit.Test(timeout = 10000)" + AmplificationHelper.LINE_SEPARATOR +
"public void test_withlog() throws java.lang.Exception {" + AmplificationHelper.LINE_SEPARATOR +
" final fr.inria.multipleobservations.ClassToBeTest classToBeTest = new fr.inria.multipleobservations.ClassToBeTest();" + AmplificationHelper.LINE_SEPARATOR +
Expand All @@ -173,7 +174,7 @@ public void testMultipleObservationsPoints() throws Exception {
public void testCreateTestWithLogClassTargetAmplify() throws Exception {
final CtMethod<?> test1 = Utils.findMethod("fr.inria.statementaddarray.TestClassTargetAmplify", "test");
final CtMethod<?> testWithLog =
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.statementaddarray");
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.statementaddarray", Collections.emptyList());
final String expectedMethod = "@org.junit.Test(timeout = 10000)" + AmplificationHelper.LINE_SEPARATOR +
"public void test_withlog() throws java.lang.Exception {" + AmplificationHelper.LINE_SEPARATOR +
" fr.inria.statementaddarray.ClassTargetAmplify clazz = new fr.inria.statementaddarray.ClassTargetAmplify();" + AmplificationHelper.LINE_SEPARATOR +
Expand All @@ -194,7 +195,7 @@ public void testCreateTestWithLog() throws Exception {
CtClass testClass = Utils.findClass("fr.inria.sample.TestClassWithoutAssert");
final CtMethod<?> test1 = (CtMethod<?>) testClass.getMethodsByName("test1").get(0);
final CtMethod<?> testWithLog =
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.sample");
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.sample", Collections.emptyList());

final String expectedMethod = "@org.junit.Test(timeout = 10000)" + AmplificationHelper.LINE_SEPARATOR +
"public void test1_withlog() throws java.lang.Exception {" + AmplificationHelper.LINE_SEPARATOR +
Expand All @@ -213,7 +214,7 @@ public void testCreateTestWithLog() throws Exception {
public void testCreateTestWithLogWithoutChainSameObservations() throws Exception {
CtMethod test1 = Utils.findMethod("fr.inria.sample.TestClassWithSpecificCaseToBeAsserted", "test1");
final CtMethod<?> testWithLog =
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.sample");
AssertGeneratorHelper.createTestWithLog(test1, "fr.inria.sample", Collections.emptyList());

final String expectedMethodWithLog = "@org.junit.Test(timeout = 10000)" + AmplificationHelper.LINE_SEPARATOR +
"public void test1_withlog() throws java.lang.Exception {" + AmplificationHelper.LINE_SEPARATOR +
Expand All @@ -238,7 +239,7 @@ public void testCreateTestWithLogWithDuplicatedStatement() throws Exception {
CtClass testClass = Utils.findClass("fr.inria.sample.TestClassWithoutAssert");
final CtMethod<?> test2 = (CtMethod<?>) testClass.getMethodsByName("test2").get(0);
final CtMethod<?> testWithLog =
AssertGeneratorHelper.createTestWithLog(test2, "fr.inria.sample");
AssertGeneratorHelper.createTestWithLog(test2, "fr.inria.sample", Collections.emptyList());

final String expectedMethod = "@org.junit.Test(timeout = 10000)" + AmplificationHelper.LINE_SEPARATOR +
"public void test2_withlog() throws java.lang.Exception {" + AmplificationHelper.LINE_SEPARATOR +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ public void testOnDifferentKindOfAssertions() throws Exception {
final CtClass<?> testClass = Utils.findClass("fr.inria.helper.TestWithMultipleAsserts");
final AssertionRemover assertionRemover = new AssertionRemover();
final CtMethod<?> testMethod = testClass.getMethodsByName("test").get(0);
System.out.println(testMethod);
final CtMethod<?> removedAssertion = assertionRemover.removeAssertion(testMethod);
System.out.println(removedAssertion);
assertEquals(3, removedAssertion.getBody().getStatements().size());
assertEquals(4, removedAssertion.getBody().getStatements().size());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public class TestWithMultipleAsserts {
public void test() {
verify(null);
assertEquals("", "");
Truth.assertThat("").isEmpty();
String s = "";
Truth.assertThat(s).isEmpty();
Truth.assertThat("").isEqualTo("");
Truth.assertThat(0.0F).isNotNaN();
System.out.println("");
Expand Down

0 comments on commit d1ccb99

Please sign in to comment.