Skip to content

Commit

Permalink
Improve json report of coverage selector and comments generated durin…
Browse files Browse the repository at this point in the history
…g assertion (#956)

* report accurate number of test classes in log, include test case information in report json of coverage selector

* comments on literals visible in resulting tests, more expressive comments

* more and more detailed comments on generated statements

move comments that would be added in the middle of a 'line' to be over that line in the resulting test case

* adjust test to new comments

* getter methods to access data from coverage data report

* remove unused imports
  • Loading branch information
lacinoire committed Jul 28, 2020
1 parent 0b0f9b1 commit d358632
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@
import eu.stamp_project.dspot.amplifier.amplifiers.value.ValueCreatorHelper;
import eu.stamp_project.dspot.common.miscellaneous.CloneHelper;
import eu.stamp_project.dspot.amplifier.amplifiers.utils.RandomHelper;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtBodyHolder;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.CtStatementList;
import eu.stamp_project.dspot.common.miscellaneous.DSpotUtils;
import spoon.reflect.code.*;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtParameter;
Expand Down Expand Up @@ -69,7 +64,8 @@ public static CtMethod<?> addInvocation(CtMethod<?> testMethod,
CtMethod<?> methodToInvokeToAdd,
CtExpression<?> target,
CtStatement position,
String suffix) {
String suffix,
String comment) {
final Factory factory = testMethod.getFactory();
CtMethod methodClone = CloneHelper.cloneTestMethodForAmp(testMethod, suffix);

Expand Down Expand Up @@ -99,14 +95,15 @@ public static CtMethod<?> addInvocation(CtMethod<?> testMethod,
localVariable = ValueCreator.createRandomLocalVar(parameter.getType(), parameter.getSimpleName());
}
body.insertBegin(localVariable);
DSpotUtils.addComment(localVariable, comment, CtComment.CommentType.INLINE);
arguments.add(factory.createVariableRead(localVariable.getReference(), false));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
CtExpression targetClone = target.clone();
CtInvocation newInvocation = factory.Code().createInvocation(targetClone, methodToInvokeToAdd.getReference(), arguments);
//DSpotUtils.addComment(newInvocation, "MethodGenerator", CtComment.CommentType.INLINE);
DSpotUtils.addComment(newInvocation, comment, CtComment.CommentType.INLINE);
body.insertEnd(newInvocation);
return methodClone;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ private CtMethod<?> createNumberMutant(CtMethod<?> method, int original_lit_inde
//get the lit_indexth literal of the cloned method
CtLiteral newLiteral = Query.getElements(cloned_method.getBody(), new LiteralToBeMutedFilter())
.get(original_lit_index);
Object oldValue = newLiteral.getValue();

CtElement toReplace = newLiteral;

Expand All @@ -110,7 +111,7 @@ private CtMethod<?> createNumberMutant(CtMethod<?> method, int original_lit_inde
}
toReplace.replace(newLiteral);
Counter.updateInputOf(cloned_method, 1);
DSpotUtils.addComment(toReplace, "FastLiteralAmplifier on numbers", CtComment.CommentType.INLINE);
addComment(toReplace, "number", oldValue, newValue);
return cloned_method;
}

Expand All @@ -132,8 +133,9 @@ private CtMethod<?> createStringMutant(CtMethod<?> method, int original_lit_inde
Counter.updateInputOf(cloned_method, 1);
CtLiteral toReplace = Query.getElements(cloned_method.getBody(), new LiteralToBeMutedFilter())
.get(original_lit_index);
Object oldValue = toReplace.getValue();
toReplace.replace(cloned_method.getFactory().Code().createLiteral(newValue));
DSpotUtils.addComment(toReplace, "FastLiteralAmplifier on strings", CtComment.CommentType.INLINE);
addComment(toReplace, "string", oldValue, newValue);
return cloned_method;
}

Expand All @@ -148,8 +150,9 @@ private CtMethod<?> createCharacterMutant(CtMethod method, int original_lit_inde
Counter.updateInputOf(cloned_method, 1);
CtLiteral toReplace = Query.getElements(cloned_method.getBody(), new LiteralToBeMutedFilter())
.get(original_lit_index);
Object oldValue = toReplace.getValue();
toReplace.replace(cloned_method.getFactory().Code().createLiteral(newValue));
DSpotUtils.addComment(toReplace, "FastLiteralAmplifier on strings", CtComment.CommentType.INLINE);
addComment(toReplace, "char", oldValue, newValue);
return cloned_method;
}

Expand Down Expand Up @@ -223,7 +226,7 @@ public boolean matches(CtLiteral element) {
newValue.setValue(!value);
newValue.setTypeCasts(booleanLiteral.getTypeCasts());
Counter.updateInputOf(cloned_method, 1);
DSpotUtils.addComment(newValue, "FastLiteralAmplifier on boolean", CtComment.CommentType.INLINE);
addComment(newValue, "boolean", value, !value);
return cloned_method;
}

Expand All @@ -243,4 +246,8 @@ private Set<Object> getLiterals(CtType type) {
}
return literalByClass.get(type);
}

private void addComment(CtElement element, String kind, Object oldValue, Object newValue) {
DSpotUtils.addComment(element, "FastLiteralAmplifier: change " + kind + " from '" + oldValue + "' to '" + newValue + "'", CtComment.CommentType.INLINE);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ public Stream<CtMethod<?>> amplify(CtMethod<?> testMethod, int iteration) {
methodToBeAdd,
AmplifierHelper.createLocalVarRef(existingObject),
existingObject,
"_mg")
"_mg",
"MethodAdderOnExistingObjectsAmplifier: added method on existing object")
).map(amplifiedTestMethod -> {
Counter.updateInputOf(amplifiedTestMethod, 1);
return amplifiedTestMethod;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package eu.stamp_project.dspot.amplifier.amplifiers;

import eu.stamp_project.dspot.common.miscellaneous.DSpotUtils;
import eu.stamp_project.dspot.common.test_framework.TestFramework;
import eu.stamp_project.dspot.amplifier.amplifiers.utils.AmplificationChecker;
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.CtExpression;
import spoon.reflect.code.CtInvocation;
import spoon.reflect.code.CtStatement;
import spoon.reflect.code.*;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtType;
import spoon.reflect.visitor.filter.TypeFilter;
Expand Down Expand Up @@ -53,6 +51,7 @@ private CtMethod<?> apply(CtMethod<?> method, CtInvocation<?> invocation) {
removeAllTypeCast(invocationToBeInserted);
final CtStatement insertionPoint = this.getRightInsertionPoint(invocation);
insertionPoint.insertBefore(invocationToBeInserted);
DSpotUtils.addComment(invocationToBeInserted, "MethodDuplicationAmplifier: duplicated method call", CtComment.CommentType.INLINE);
final CtMethod<?> clone = CloneHelper.cloneTestMethodForAmp(method, "_add");
AmplifierHelper.getParent(invocationToBeInserted).getStatements().remove(invocationToBeInserted);
Counter.updateInputOf(clone, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public Stream<CtMethod<?>> amplify(CtMethod<?> testMethod, int iteration) {
);
DSpotUtils.addComment(localVar, "StatementAdd: generate variable from return value", CtComment.CommentType.INLINE);
ampMethods.addAll(methodsWithTargetType.stream()
.map(addMth -> AmplifierHelper.addInvocation(methodClone, addMth, target, localVar, "_rv"))
.map(addMth -> AmplifierHelper.addInvocation(methodClone, addMth, target, localVar, "_rv", "ReturnValueAmplifier: add method call"))
.collect(Collectors.toList()));
Counter.updateInputOf(methodClone, 1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.Query;
import spoon.reflect.visitor.filter.TypeFilter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import java.util.*;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -120,23 +118,23 @@ private CtMethod<?> buildTestWithAssert(CtMethod test, Map<String, Observation>
an assertion identical to the last assertion put into the test method */
if (assertStatements.stream()
.map(Object::toString)
.map("// AssertionGenerator add assertion\n"::concat)
.map("// AssertionGenerator: add assertion\n"::concat)
.anyMatch(testWithAssert.getBody().getLastStatement().toString()::equals)) {
continue;
}
goThroughAssertionStatements(assertStatements,id,statements,numberOfAddedAssertion);
numberOfAddedAssertion = goThroughAssertionStatements(assertStatements,id,statements,numberOfAddedAssertion);
}
Counter.updateAssertionOf(testWithAssert, numberOfAddedAssertion);
return decideReturn(testWithAssert,test);
}

private void goThroughAssertionStatements(List<CtStatement> assertStatements,String id,
List<CtStatement> statements,Integer numberOfAddedAssertion){
private int goThroughAssertionStatements(List<CtStatement> assertStatements,String id,
List<CtStatement> statements, int numberOfAddedAssertion){
int line = Integer.parseInt(id.split("__")[1]);
CtStatement lastStmt = null;
for (CtStatement assertStatement : assertStatements) {
DSpotUtils.addComment(assertStatement,
"AssertionGenerator add assertion",
"AssertionGenerator: add assertion",
CtComment.CommentType.INLINE);
try {
CtStatement statementToBeAsserted = statements.get(line);
Expand All @@ -153,6 +151,7 @@ private void goThroughAssertionStatements(List<CtStatement> assertStatements,Str
throw new RuntimeException(e);
}
}
return numberOfAddedAssertion;
}

private void decideInvocationReplacement(CtStatement statementToBeAsserted,String id,CtStatement assertStatement,
Expand Down Expand Up @@ -184,8 +183,15 @@ private void replaceInvocation(CtStatement statementToBeAsserted,String id,CtSta

// put the new local variable into the assertion and the assertion into the test method
statementToBeAsserted.replace(localVariable);
// move comments from invocation to new variable statement
List<CtComment> invocationComments = new ArrayList<>(statementToBeAsserted.getComments());
invocationComments.forEach(comment -> {
invocationToBeReplaced.removeComment(comment);
localVariable.addComment(comment);
});

DSpotUtils.addComment(localVariable,
"AssertionGenerator create local variable with return value of invocation",
"AssertionGenerator: create local variable with return value of invocation",
CtComment.CommentType.INLINE);
localVariable.setParent(statementToBeAsserted.getParent());
addAtCorrectPlace(id, localVariable, assertStatement, statementToBeAsserted);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,24 @@ public static Integer getAssertionOfSinceOrigin(CtMethod method) {
CtMethod<?> parent;
int countAssertion = getAssertionOf(currentMethod);
while ((parent = AmplificationHelper.getAmpTestParent(currentMethod)) != null) {
if (!parent.getSimpleName().equals(currentMethod.getSimpleName())) {
countAssertion += getAssertionOf(parent);
}
currentMethod = parent;
countAssertion += getAssertionOf(currentMethod);
}
return countAssertion;
}

public static Integer getInputOfSinceOrigin(CtMethod method) {
CtMethod currentMethod = method;
CtMethod parent;
int countAssertion = 0;
int countAssertion = getInputOf(currentMethod);
while ((parent = AmplificationHelper.getAmpTestParent(currentMethod)) != null) {
countAssertion += getInputOf(currentMethod);
if (!parent.getSimpleName().equals(currentMethod.getSimpleName())) {
countAssertion += getInputOf(parent);
}
currentMethod = parent;
}
countAssertion += getInputOf(currentMethod);
return countAssertion;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@
import spoon.Launcher;
import spoon.compiler.Environment;
import spoon.reflect.code.CtComment;
import spoon.reflect.code.CtLiteral;
import spoon.reflect.declaration.*;
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.filter.LineFilter;
import spoon.support.JavaOutputProcessor;

import java.io.*;
Expand Down Expand Up @@ -175,6 +177,17 @@ private static CtClass<?> getExistingClass(CtType<?> type, String pathname) {
}

public static void addComment(CtElement element, String content, CtComment.CommentType type) {
if (element instanceof CtLiteral) {
try {
CtElement parentLine = element.getParent(new LineFilter());
if (parentLine != null) {
element = parentLine;
}
} catch (ParentNotInitializedException ignored) {

}
}

CtComment comment = element.getFactory().createComment(content, type);
if (!element.getComments().contains(comment)) {
element.addComment(comment);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,10 @@ private void outputSeedTestClassWithSuccessTestMethods(CtType<?> testClassToBeAm
.distinct()
.forEach(clone::addMethod);
final File outputDirectory = new File(this.outputPathDirectory + "/original/");
DSpotState.GLOBAL_REPORT.addNumberAmplifiedTestMethodsToTotal(amplifiedTestMethods.size());
DSpotState.GLOBAL_REPORT.addPrintedTestClasses(
String.format("Print %s with %d amplified test cases in %s",
String.format("Print %s with %d original test cases in %s",
clone.getQualifiedName() + ".java",
amplifiedTestMethods.size(),
clone.getMethods().size(),
this.outputPathDirectory + "/original/"
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,24 @@ public TestCaseJSON(String name, int nbAssertionAdded, int nbInputAdded, int ins
this.instructionCovered = instructionCovered;
this.instructionTotal = instructionTotal;
}

public String getName() {
return name;
}

public int getNbAssertionAdded() {
return nbAssertionAdded;
}

public int getNbInputAdded() {
return nbInputAdded;
}

public int getInstructionCovered() {
return instructionCovered;
}

public int getInstructionTotal() {
return instructionTotal;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,40 @@ public String toString() {
String jsonString = new JSONObject().put(this.name,subJson).toString();
return jsonString;
}

public String getName() {
return name;
}

public long getNbOriginalTestCases() {
return nbOriginalTestCases;
}

public long getInitialInstructionCovered() {
return initialInstructionCovered;
}

public long getInitialInstructionTotal() {
return initialInstructionTotal;
}

public double getPercentageinitialInstructionCovered() {
return percentageinitialInstructionCovered;
}

public long getAmplifiedInstructionCovered() {
return amplifiedInstructionCovered;
}

public long getAmplifiedInstructionTotal() {
return amplifiedInstructionTotal;
}

public double getPercentageamplifiedInstructionCovered() {
return percentageamplifiedInstructionCovered;
}

public List<TestCaseJSON> getTestCases() {
return testCases;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,12 @@ private TestClassJSON jsonReport(Coverage coverageResults) {
);
}
this.selectedAmplifiedTest.forEach(ctMethod ->
new TestCaseJSON(ctMethod.getSimpleName(),
Counter.getInputOfSinceOrigin(ctMethod),
testClassJSON.addTestCase(new TestCaseJSON(ctMethod.getSimpleName(),
Counter.getAssertionOfSinceOrigin(ctMethod),
Counter.getInputOfSinceOrigin(ctMethod),
this.selectedToBeAmplifiedCoverageResultsMap.get(ctMethod.getSimpleName()).getInstructionsCovered(),
this.selectedToBeAmplifiedCoverageResultsMap.get(ctMethod.getSimpleName()).getInstructionsTotal()
)
))
);
// TODO
// CollectorConfig.getInstance().getInformationCollector().reportSelectorInformation(testClassJSON.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ public void testMethodCallAddAll() throws Exception {
for (int i = 0; i < amplifiedMethods.size(); i++) {
CtMethod amplifiedMethod = amplifiedMethods.get(i);
assertEquals(originalMethod.getBody().getStatements().size() + 1, amplifiedMethod.getBody().getStatements().size());
CtStatement expectedStatement = originalMethod.getBody().getStatements().get(i + 1);//+1 to skip the construction statement.
assertEquals(expectedStatement.toString(),
CtStatement expectedStatement =originalMethod.getBody().getStatements().get(i + 1);//+1 to skip the construction statement.
assertEquals( "// MethodDuplicationAmplifier: duplicated method call\n" + expectedStatement.toString(),
amplifiedMethod.getBody().getStatements().get(i + 1).toString());
assertEquals(expectedStatement.toString(),
amplifiedMethod.getBody().getStatements().get(i + 2).toString());
Expand Down

0 comments on commit d358632

Please sign in to comment.