1

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:

  1. a==2
  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 either
  • BinaryExpr.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!

  • "The expression I want to get after the mutation would be `a==2 b==2`" But that is not an expression, according to Java's syntax. It is not even syntactically valid. Rather than saying what expression you want, can you describe the resulting AST that you want? – Sweeper Aug 21 '21 at 09:49
  • Edited just now, thanks! – 9595kuba9595 Aug 21 '21 at 11:25
  • Oh I see. So you want to turn `a==2 & b==2` to either `a==2` or `b==2`? – Sweeper Aug 21 '21 at 11:27
  • Yes. The most obvious way of doing that is `childNode.replace(parentNode)`, or `childNode.setParentNode(childNode)`, but this does not seem to work. – 9595kuba9595 Aug 21 '21 at 11:32
  • I suggest that you add the code that you used to your question. – Sweeper Aug 21 '21 at 11:55

1 Answers1

0

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"));
Sweeper
  • 213,210
  • 22
  • 193
  • 313