I am doing some work on mutation testing at the Abstract Syntax Tree level. So far, I have dealt with Operator Replacement mutations, which were fairly easy to implement using the JavaParser library (https://github.com/javaparser/javaparser). Below is the simple, example code that I am parsing into the AST and trying to perform mutations on it. I know the if statement does not make sense but it is not the point here:
public class Example {
public static void main(String[] args) {
int a=2;
int b=3;
if (a==2 & b==2)
doSomething();
}
}
My program parses the java code I have (in this case, Example.java) into an AST in the form of a CompilationUnit
. Then I traverse the CompilationUnit
looking for a specific expression, for example a binary expression BinaryExpr
. Once it is found, I try to remove the binary operator BinaryExpr.Operator.<Operator>
from that expression. When a binary operator is removed, an operand must also be removed so the expression remains compilable. Therefore, removing the binary operator from the above a==2 & b==2
expression will result in two mutants:
a==2
b==2
Therefore, it seems like I have to replace a parent with one of the children.
However, I am now struggling with this part and I do not know how to implement that. I have so far tried the following:
childNode.replace(parentNode)
-> replace the parentNode ( a==2 & b==2) with one of its children (either a==2 or b==2) -> this gives no error but the parent does not change. WHY?childNode.setParentNode(childNode)
-> no exceptions but parent does not change eitherBinaryExpr.replace(leftChild, rightChild)
--> this one was the only one that actually worked and changed the parent node, but obviously the operator was still there.Can anyone suggest a solution to this problem? I want to be able to remove the operator from the expression, and I am not sure how to do that.
This is also my first question asked on StackOverflow so apologies if it is a bit messy.
Thank you!
According to the documentation of ModifierVisitor
:
This visitor can be used to save time when some specific nodes needs to be changed. To do that just extend this class and override the methods from the nodes who needs to be changed, returning the changed node. Returning null will remove the node.
In your case, if a node that should be replaced is found, the "changed node" would be the left/right node, so you should return those accordingly.
n.replace(leftChild)
is working correctly. You just need to return the new node that is in its place, which you probably didn't do.
Here's an example:
SourceRoot sourceRoot = new SourceRoot(Paths.get("Foo.java"));
CompilationUnit cu = sourceRoot.parse("", "Foo.java");
cu.accept(new ModifierVisitor<Void>() {
@Override
public Visitable visit(BinaryExpr n, Void arg) {
if (n.getOperator() == Operator.BINARY_AND) {
n.replace(n.getLeft());
return n.getLeft(); // note this line!
}
return super.visit(n, arg);
}
}, null);
sourceRoot.saveAll(Paths.get("Foo.java"));