Details for exercise 5

The @ASTTest AST transformation

The @ASTTest AST transformation is a very interesting tool. First of all, it is itself an AST transformation, but it is aimed towards testing other AST transformations. It takes two arguments, which are: * a compile phase, corresponding to the compile phase after which we want to test the AST * a value which is code that can contain assertions, but which is executed against the AST

Take the following example:

@ASTTest(phase=SEMANTIC_ANALYSIS, value={
   assert node.name == 'Person'
})
class Person {
}

What it does is during the compilation process, after the SEMANTIC_ANALYSIS phase has run, check that the annotated node (node) has the name Person. Of course, the test is not really useful here, but it allows you to understand the logic. Should the assertion fail, then compilation would fail.

The @ASTTest transformation exposes two things in the script: * the node variable, which refers to the AST node which is annotated with @ASTTest * the lookup method, which is an utility method that can help you find a particular AST node when you want to use @ASTTest on AST nodes which do not support annotations. For example:

@ASTTest(phase=INSTRUCTION_SELECTION, value= {
    lookup('forLoop').each {
        assert it instanceof org.codehaus.groovy.ast.stmt.ForStatement
        def collection = it.collectionExpression // MethodCallExpression
    }
})
void test() {
    def result = ""
    def sum = 0
    forLoop:
    for ( Map.Entry<String, Integer> it in [a:1, b:3].entrySet() ) {
        result += it.getKey()
        sum += it.getValue()
    }
    assert result == "ab"
    assert sum == 4
}

The lookup call will allow you to find the node which label is forLoop.

Tips and tricks

  • Use static imports to remove boilerplate in your ASTTest code

Comments

What is the main difference between @ASTTest and the previous tests we've written?

The main difference is that ASTTest allows you to test the actual result of your AST transformation as an AST tree, while regular tests only check the behaviour at runtime. Using @ASTTest allows you to test things which are no more available at runtime (for example, @TypeChecked uses it intensively to check the inferred types).

What solution is preferred?

Both. You should always test both the AST tree and the behaviour. They are testing different things, so you need both @ASTTest and runtime tests.