Commits

Anonymous committed e5ab7c6

Adding code for AST transformation examples

Comments (0)

Files changed (47)

Groovy/ast/build.gradle

+apply plugin: 'groovy'
+
+repositories {
+    mavenCentral()
+}
+
+dependencies {
+    compile 'org.codehaus.groovy:groovy-all:2.2.0'
+    compile 'org.slf4j:slf4j-api:1.7.5'
+    compile 'org.slf4j:slf4j-simple:1.7.5'
+    testCompile 'org.spockframework:spock-core:0.7-groovy-2.0'
+    testCompile 'org.assertj:assertj-core:1.5.0'
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/ast/WithAroundASTTransformation.groovy

+package com.blogspot.toomuchcoding.ast
+import org.codehaus.groovy.ast.ASTNode
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.ast.MethodNode
+import org.codehaus.groovy.ast.builder.AstBuilder
+import org.codehaus.groovy.ast.expr.ArgumentListExpression
+import org.codehaus.groovy.ast.expr.ConstantExpression
+import org.codehaus.groovy.ast.expr.MethodCallExpression
+import org.codehaus.groovy.ast.expr.VariableExpression
+import org.codehaus.groovy.ast.stmt.ExpressionStatement
+import org.codehaus.groovy.ast.stmt.Statement
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.transform.ASTTransformation
+import org.codehaus.groovy.transform.GroovyASTTransformation
+
+import static org.codehaus.groovy.control.CompilePhase.SEMANTIC_ANALYSIS
+
+@GroovyASTTransformation(phase = SEMANTIC_ANALYSIS)
+public class WithAroundASTTransformation implements ASTTransformation {
+
+    public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
+        // get methods from the annotated object
+        List methods = sourceUnit.getAST()?.getMethods()
+        // find all methods annotated with WithLogging
+        methods.findAll { MethodNode method ->
+            method.getAnnotations(new ClassNode(WithLogging))
+        }.each { MethodNode method ->
+            addAstStatementsInAManualWay(method)
+            //addAstStatementsWithBuilderFromString(method)
+            //addAstStatementsWithBuilderFromCode(method)
+            //addAstStatementsWithBuilderFromSpec(method)
+        }
+    }
+    
+    // MANUAL
+    
+    private void addAstStatementsInAManualWay(MethodNode method) {
+        Statement startMessage = createPrintlnAst("Starting $method.name")
+        Statement endMessage = createPrintlnAst("Ending $method.name")
+        addStatements(method, startMessage, endMessage)   
+    }
+
+    private Statement createPrintlnAst(String message) {                       
+        // create an expression
+        return new ExpressionStatement(
+                // that executes a method
+                new MethodCallExpression(
+                        // this.println( ... )
+                        new VariableExpression("this"),
+                        new ConstantExpression("println"),
+                        // that takes message as an argument
+                        new ArgumentListExpression(
+                                new ConstantExpression(message)
+                        )
+                )
+        )
+    }
+
+    // FROM STRING    
+    
+    private void addAstStatementsWithBuilderFromString(MethodNode method) {
+        Statement startMessage = createPrintlnAstWithAstBuilderFromString("Starting $method.name")
+        Statement endMessage = createPrintlnAstWithAstBuilderFromString("Ending $method.name")
+        addStatements(method, startMessage, endMessage)
+    }
+
+    private Statement createPrintlnAstWithAstBuilderFromString(String message) {
+        def buildNodes = new AstBuilder().buildFromString(SEMANTIC_ANALYSIS, false, "println(\"$message\")")
+        return buildNodes[0].statements[0]
+    }
+    
+    // FROM CODE
+
+    private void addAstStatementsWithBuilderFromCode(MethodNode method) {
+        Statement startMessage = createPrintlnAstWithAstBuilderFromCode("Starting $method.name")
+        Statement endMessage = createPrintlnAstWithAstBuilderFromCode("Ending $method.name")
+        addStatements(method, startMessage, endMessage)
+    }
+
+    private Statement createPrintlnAstWithAstBuilderFromCode(String message) {
+        def buildNodes = new AstBuilder().buildFromCode(SEMANTIC_ANALYSIS, false, {  
+            println message
+        })
+        return buildNodes[0].statements[0]
+    }
+    
+    
+    // FROM SPECIFICATION
+
+    private void addAstStatementsWithBuilderFromSpec(MethodNode method) {
+        Statement startMessage = createPrintlnAstWithAstBuilderFromSpec("Starting $method.name")
+        Statement endMessage = createPrintlnAstWithAstBuilderFromSpec("Ending $method.name")
+        addStatements(method, startMessage, endMessage)
+    }
+
+    private Statement createPrintlnAstWithAstBuilderFromSpec(String message) {
+        def buildNodes = new AstBuilder().buildFromSpec ({
+            methodCall {
+                variable "this"
+                constant "println"
+                argumentList {
+                    constant message
+                }
+            }  
+        })
+        return new ExpressionStatement(buildNodes[0])
+    }
+
+    private void addStatements(MethodNode method, Statement startMessage, Statement endMessage) {
+        // Method code consists of statements
+        List<ASTNode> existingStatements = method.getCode().getStatements()
+        // Start msg at the beginning of the code block
+        existingStatements.add(0, startMessage)
+        // End msg at the end of the block code
+        existingStatements.add(endMessage)
+    }
+    
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/ast/WithLogging.groovy

+package com.blogspot.toomuchcoding.ast
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass
+
+import java.lang.annotation.ElementType
+import java.lang.annotation.Retention
+import java.lang.annotation.RetentionPolicy
+import java.lang.annotation.Target
+
+@Retention(RetentionPolicy.SOURCE)
+@Target([ElementType.METHOD])
+@GroovyASTTransformationClass(["com.blogspot.toomuchcoding.ast.WithAroundASTTransformation"])
+public @interface WithLogging {
+
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/basescript/NewBaseScript.groovy

+package com.blogspot.toomuchcoding.basescript
+
+class NewBaseScript extends Script {
+
+    static final String OUTPUT_MSG = "Inner hello"
+
+    String hello() {
+        return OUTPUT_MSG
+    }
+
+    @Override
+    Object run() {
+        return "script"
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/canonical/CanonicalPlayer.groovy

+package com.blogspot.toomuchcoding.canonical
+
+import groovy.transform.Canonical
+
+@Canonical
+class CanonicalPlayer {
+    String name, surname
+    Collection positions
+    int age
+    Map skills
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/category/AnnotatedTensCategory.groovy

+package com.blogspot.toomuchcoding.category
+
+@Category(Integer)
+class AnnotatedTensCategory {
+    Integer getAnnotatedTens() {
+        return this * 10
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/category/TensCategory.groovy

+package com.blogspot.toomuchcoding.category
+
+class TensCategory {
+    static Integer getTens(Integer self) {
+        return self * 10
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/compilestatic/CompileStaticAndTypeChecked.groovy

+package com.blogspot.toomuchcoding.compilestatic
+
+import groovy.transform.CompileStatic
+import groovy.transform.TypeChecked
+
+class CompileStaticAndTypeChecked {
+    DynamicPlayer player
+
+    CompileStaticAndTypeChecked() {
+        player = new DynamicPlayer()
+    }
+
+    /**
+     * There is no method bar yet the code compiles - try to add @TypeChecked or @CompileStatic and the code won't compile
+     * @return
+     */
+    def groovy() {
+        player.bar
+    }
+
+    @TypeChecked
+    def typed() {
+        player.unknownType
+    }
+
+    @CompileStatic
+    def compiled() {
+        player.unknownType
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/compilestatic/DynamicPlayer.groovy

+package com.blogspot.toomuchcoding.compilestatic
+
+class DynamicPlayer {
+    static final String ORIGINAL_RESULT = "bar"
+    
+    def unknownType = ORIGINAL_RESULT
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/delegate/DelegatePlayer.groovy

+package com.blogspot.toomuchcoding.delegate
+
+class DelegatePlayer {
+    String name, surname
+    @Delegate
+    BigDecimal value
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/equalshashcode/EqualsAndHashCodePlayer.groovy

+package com.blogspot.toomuchcoding.equalshashcode
+import groovy.transform.EqualsAndHashCode
+
+@EqualsAndHashCode
+class EqualsAndHashCodePlayer {
+    String name, surname
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/externalized/ExternalizedPlayer.groovy

+package com.blogspot.toomuchcoding.externalized
+
+import groovy.transform.AutoExternalize
+
+@AutoExternalize
+class ExternalizedPlayer {
+
+    static final long serialVersionUID = 1L
+
+    String name
+    String surname
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/immutable/ImmutablePlayer.groovy

+package com.blogspot.toomuchcoding.immutable
+
+import groovy.transform.Immutable
+
+@Immutable
+class ImmutablePlayer {
+    String name
+    MutablePlayer mutablePlayer
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/immutable/MutablePlayer.groovy

+package com.blogspot.toomuchcoding.immutable
+
+class MutablePlayer {
+    int age
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/inheritconstructors/MyRuntimeException.groovy

+package com.blogspot.toomuchcoding.inheritconstructors
+
+/**
+ * You have to write the constructors yourself or make IDE generate it for you...
+ */
+class MyRuntimeException extends RuntimeException {
+    MyRuntimeException() {
+    }
+
+    MyRuntimeException(String s) {
+        super(s)
+    }
+
+    MyRuntimeException(String s, Throwable throwable) {
+        super(s, throwable)
+    }
+
+    MyRuntimeException(Throwable throwable) {
+        super(throwable)
+    }
+
+    MyRuntimeException(String s, Throwable throwable, boolean b, boolean b1) {
+        super(s, throwable, b, b1)
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/inheritconstructors/MySuperRuntimeException.groovy

+package com.blogspot.toomuchcoding.inheritconstructors
+
+import groovy.transform.InheritConstructors
+
+@InheritConstructors
+class MySuperRuntimeException extends RuntimeException {
+
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/loggers/PlayerServiceWithLog.groovy

+package com.blogspot.toomuchcoding.loggers
+
+import groovy.util.logging.Slf4j
+
+@Slf4j
+class PlayerServiceWithLog {
+    void someMethod() {
+        log.info("THANK GOD!!!!!!!!!")
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/loggers/PlayerServiceWithManualLog.groovy

+package com.blogspot.toomuchcoding.loggers
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+class PlayerServiceWithManualLog {
+    private static final Logger log = LoggerFactory.getLogger(PlayerServiceWithManualLog)
+
+    void someMethod() {
+        log.info("HOW MANY TIMES DID YOU HAVE TO WRITE THAT LOGGER?")
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/mixin/Athlete.groovy

+package com.blogspot.toomuchcoding.mixin
+
+interface Athlete {
+    String getName()
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/mixin/DribblingAbility.groovy

+package com.blogspot.toomuchcoding.mixin
+
+@Category(Athlete)
+class DribblingAbility {
+    def dribble() { 
+        return "My name is [${name}]. I can dribble." 
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/mixin/FootballPlayer.groovy

+package com.blogspot.toomuchcoding.mixin
+
+@Mixin([RunningAbility, DribblingAbility])
+class FootballPlayer implements Athlete {
+    String getName() {
+        return "Robert Lewandowski" 
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/mixin/RunningAbility.groovy

+package com.blogspot.toomuchcoding.mixin
+
+@Category(Athlete)
+class RunningAbility {
+    def run() {
+        return "My name is [${name}]. I can run." 
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/mixin/TennisAbility.groovy

+package com.blogspot.toomuchcoding.mixin
+
+@Category(Athlete)
+class TennisAbility {
+    def backhand() {
+        return "My name is [${name}]. I can use my backhand." 
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/mixin/TennisPlayer.groovy

+package com.blogspot.toomuchcoding.mixin
+
+@Mixin([RunningAbility, TennisAbility])
+class TennisPlayer implements Athlete {
+    String getName() {
+        return "Lukasz Kubot" 
+    }
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/tostring/BasePlayer.groovy

+package com.blogspot.toomuchcoding.tostring
+
+import groovy.transform.ToString
+
+@ToString(includePackage = false)
+class BasePlayer {
+    String name, surname
+}

Groovy/ast/src/main/groovy/com/blogspot/toomuchcoding/tostring/ChildPlayer.groovy

+package com.blogspot.toomuchcoding.tostring
+
+import groovy.transform.ToString
+
+@ToString(includeSuper = true, includeNames = true)
+class ChildPlayer extends BasePlayer {
+    int age
+}

Groovy/ast/src/main/resources/ast/ClassWithAst.groovy

+package ast
+
+import com.blogspot.toomuchcoding.ast.WithLogging
+
+@WithLogging()
+void doSth() {
+    println "In the middle"
+}
+
+doSth()

Groovy/ast/src/main/resources/baseScript/SomeScript.groovy

+package baseScript
+
+import com.blogspot.toomuchcoding.basescript.NewBaseScript
+import groovy.transform.BaseScript
+
+println "Hello"
+
+@BaseScript NewBaseScript newBaseScript
+
+assert newBaseScript == this
+return hello()

Groovy/ast/src/main/resources/field/FieldScript.groovy

+package field
+
+import groovy.transform.Field
+
+@Field def newMap = ["key" : "value"]
+
+String someOtherMethod() {
+    return newMap["key"]
+}
+
+return someOtherMethod()

Groovy/ast/src/main/resources/field/WrongFieldScript.groovy

+package field
+
+def map = ["key": "value"]
+
+String someMethod() {
+    return map["key"]
+}
+
+return someMethod()

Groovy/ast/src/main/resources/logback.xml

+<configuration>
+
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <!-- encoders are assigned the type
+             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="debug">
+        <appender-ref ref="STDOUT" />
+    </root>
+</configuration>

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/AutoExternalizeSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.externalized.ExternalizedPlayer
+import com.blogspot.toomuchcoding.util.SerializationUtil
+import spock.lang.Specification
+
+class AutoExternalizeSpec extends Specification {
+
+    def "should serialize and deserialize object"() {        
+        given:
+            ExternalizedPlayer objectUnderTest = new ExternalizedPlayer(name: "name", surname: "surname")
+        
+        when:
+            def deserializedObject = SerializationUtil.serializeAndBack(objectUnderTest)
+        
+        then:
+            deserializedObject.name == objectUnderTest.name
+            deserializedObject.surname == objectUnderTest.surname
+    }
+    
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/BaseScriptSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.basescript.NewBaseScript
+import spock.lang.Specification
+
+class BaseScriptSpec extends Specification {
+    
+    def "should make script return the @BaseScript annotated object"() {
+        given:
+            Class baseScript =  new GroovyClassLoader().parseClass(this.getClass().getResource("/baseScript/SomeScript.groovy").text)
+            Script script = (Script) baseScript.newInstance()
+        when:
+            def result = script.run()
+        then:
+            NewBaseScript.OUTPUT_MSG == result
+    }
+    
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/CanonicalSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.canonical.CanonicalPlayer
+import spock.lang.Specification
+
+class CanonicalSpec extends Specification {
+    
+    def "should return equal objects by instantiation using map arguments and created constructor"() {
+        given:
+            def player = new CanonicalPlayer(name: "name", surname: "surname", positions: ["forward", "defence"], age: 10, skills: ["offence": "good", "defence": "bad"])
+        when:            
+            def canonicalPlayer = new CanonicalPlayer("name", "surname", ["forward", "defence"], 10, ["offence": "good", "defence": "bad"])
+        then:
+            player == canonicalPlayer
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/CategorySpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.category.AnnotatedTensCategory
+import com.blogspot.toomuchcoding.category.TensCategory
+import spock.lang.Specification
+
+class CategorySpec extends Specification {
+    
+    def "should mix in new functionality using TensCategory"() {        
+        when:
+            def result
+            use(TensCategory) {
+                result = 4.tens
+            }
+        then:
+            40 == result
+
+    }
+    
+    def "should mix in new functionality using AnnotatedTensCategory"() {        
+        when:
+            def result
+            use(AnnotatedTensCategory) {
+                result = 4.annotatedTens
+            }
+        then:
+            40 == result
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/CompileStaticAndTypeCheckSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.compilestatic.CompileStaticAndTypeChecked
+import com.blogspot.toomuchcoding.compilestatic.DynamicPlayer
+import spock.lang.Specification
+
+import static com.blogspot.toomuchcoding.compilestatic.DynamicPlayer.getORIGINAL_RESULT
+
+class CompileStaticAndTypeCheckSpec extends Specification {
+    
+    static final String OVERLOADED_RESULT = ""
+    
+    def setupSpec() {
+        DynamicPlayer.metaClass.getUnknownType = { OVERLOADED_RESULT }
+    }
+    
+    def "should return injected method's result for @TypeChecked annotated method"() {
+        given:
+            CompileStaticAndTypeChecked object = new CompileStaticAndTypeChecked()        
+        when:
+            String result = object.typed()
+        then:
+            OVERLOADED_RESULT == result
+    }
+
+    def "should return original method's result for @CompileStatic annotated method"() {
+        given:
+            CompileStaticAndTypeChecked object = new CompileStaticAndTypeChecked()
+        when:
+            String result = object.compiled()
+        then:
+            ORIGINAL_RESULT == result
+    }
+
+    def cleanupSpec() {
+        DynamicPlayer.metaClass = null
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/CustomAstSpec.groovy

+package com.blogspot.toomuchcoding
+
+import spock.lang.Specification
+
+class CustomAstSpec extends Specification {
+    ByteArrayOutputStream outContent = new ByteArrayOutputStream()    
+    
+    def setup() {
+        System.out = new PrintStream(outContent)
+    }
+    
+    def "should print content of the method in the script together with the logic from AST transform"() {
+        given: 
+            Class baseScript =  new GroovyClassLoader().parseClass(this.getClass().getResource("/ast/ClassWithAst.groovy").text)
+            Script script = (Script) baseScript.newInstance()
+        when:
+            script.run()
+        then:
+            "Starting doSth\r\n"+
+            "In the middle\r\n"+
+            "Ending doSth\r\n" == outContent.toString()
+    }
+    
+    def cleanup() {
+        System.out = null
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/DelegateSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.delegate.DelegatePlayer
+import spock.lang.Specification
+
+class DelegateSpec extends Specification {
+
+    public static final int ROBERT_VALUE = 30_000_000
+    public static final int WOJCIECH_VALUE = 10_000_000
+
+    def "should calculate total amount by allowing a player to call big decimal methods"() {
+        given:
+            DelegatePlayer robert = new DelegatePlayer(name: "robert", surname: "lewandowski", value: ROBERT_VALUE)
+            DelegatePlayer wojciech = new DelegatePlayer(name: "wojciech", surname: "szczesny", value: WOJCIECH_VALUE)
+        when:
+            BigDecimal totalAmount = robert.add(wojciech.value)
+        then:
+            totalAmount == ROBERT_VALUE + WOJCIECH_VALUE
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/EqualsAndHashCodeSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.equalshashcode.EqualsAndHashCodePlayer
+import spock.lang.Specification
+
+class EqualsAndHashCodeSpec extends Specification {
+    
+    def "should return true for equal player objects "() {
+        given:
+            EqualsAndHashCodePlayer player = new EqualsAndHashCodePlayer(name: "name", surname: "surname") 
+            EqualsAndHashCodePlayer player2 = new EqualsAndHashCodePlayer(name: "name", surname: "surname")
+        expect:
+            player == player2 
+    }
+    
+    def "should insert one entry in map for the same player"() {
+        given:
+            EqualsAndHashCodePlayer player = new EqualsAndHashCodePlayer(name: "name", surname: "surname")
+            def map = [(player): "one"]
+        when:
+            map[player] = "two"
+        then:
+            map.size() == 1
+            map[player] == "two"
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/FieldSpec.groovy

+package com.blogspot.toomuchcoding
+
+import spock.lang.Specification
+
+class FieldSpec extends Specification {
+    def "should throw exception due to undefined variable"() {
+        given:
+            Class baseScript =  new GroovyClassLoader().parseClass(this.getClass().getResource("/field/WrongFieldScript.groovy").text)
+            Script script = (Script) baseScript.newInstance()
+        when:
+            script.run()
+        then:
+            thrown(MissingPropertyException)
+    }
+    
+    def "should return value from @Field annotated map"() {
+        given:
+            Class baseScript =  new GroovyClassLoader().parseClass(this.getClass().getResource("/field/FieldScript.groovy").text)
+            Script script = (Script) baseScript.newInstance()
+        when:
+            def result = script.run()
+        then:
+            result == "value"
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/ImmutableSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.immutable.ImmutablePlayer
+import com.blogspot.toomuchcoding.immutable.MutablePlayer
+import spock.lang.Specification
+
+class ImmutableSpec extends Specification {
+    
+    def "should not allow to construct an immutable object with a mutable property"() {
+        when:
+            new ImmutablePlayer("name", new MutablePlayer(age: 10))
+        then:
+            RuntimeException exception = thrown()
+            exception.message.contains("@Immutable processor doesn't know how to handle field 'mutablePlayer' of type")
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/InheritConstructorsSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.inheritconstructors.MySuperRuntimeException
+import spock.lang.Specification
+
+class InheritConstructorsSpec extends Specification {
+    
+    def "should instantiate exceptions"() {    
+        expect:
+            new MySuperRuntimeException()
+            new MySuperRuntimeException("msg")
+            new MySuperRuntimeException("msg", new RuntimeException())
+            new MySuperRuntimeException(new RuntimeException())
+            new MySuperRuntimeException("msg", new RuntimeException(), true, false)
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/LogSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.loggers.PlayerServiceWithLog
+import spock.lang.Specification
+
+class LogSpec extends Specification {
+    def "should add logger through ast"() {
+        expect:
+            PlayerServiceWithLog.log != null        
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/MixinSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.mixin.FootballPlayer
+import com.blogspot.toomuchcoding.mixin.TennisPlayer
+import spock.lang.Specification
+
+class MixinSpec extends Specification {
+    def "should make football player run"() {
+        given:
+            def player = new FootballPlayer() 
+        expect: 
+            player.run() =~ /^.*run.$/
+    }
+    
+    def "should make football player dribble"() {
+        given:
+            def player = new FootballPlayer()
+        expect:
+            player.dribble() =~ /^.*dribble.$/
+    }
+    
+    def "should make tennis player run"() {
+        given:
+            def player = new TennisPlayer()
+        expect:
+            player.run() =~ /^.*run.$/
+    }
+    
+    def "should make tennis player dribble"() {
+        given:
+            def player = new TennisPlayer()
+        expect:
+            player.backhand() =~ /^.*backhand.$/
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/TensSpec.groovy

+package com.blogspot.toomuchcoding
+
+import spock.lang.Specification
+
+class TensSpec extends Specification {
+    
+    def "should calculate ten times given number"() {        
+        given:
+            int initialNumber = 4
+        when:
+            int calculatedNumber = getTimesTenOf(initialNumber)
+        then:
+            40 == calculatedNumber       
+    }
+
+    Integer getTimesTenOf(Integer number) {
+        return 10 * number;
+    }
+    
+    def "should calculate ten times given number by mixing a method to object"() {
+        given:
+            int initialNumber = 4
+        and:
+            initialNumber.metaClass.getTimesTen = {
+                return 10 * delegate
+            }
+        when:
+            int calculatedNumber = 4.timesTen
+        then:
+            40 == calculatedNumber
+    }
+    
+    def "should calculate ten times given number by mixing a method to class"() {
+        given:
+            Integer.metaClass.getTimesTen = {
+                return 10 * delegate
+            }
+        when:
+            int calculatedNumber = 4.timesTen
+        then:
+            40 == calculatedNumber
+    }
+    
+    def cleanup() {
+        Integer.metaClass = null    
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/ToStringSpec.groovy

+package com.blogspot.toomuchcoding
+
+import com.blogspot.toomuchcoding.tostring.ChildPlayer
+import spock.lang.Specification
+
+class ToStringSpec extends Specification {
+    
+    def "should return nice looking to string"() {
+        given:
+            ChildPlayer childPlayer = new ChildPlayer(name: "Name", surname: "Surname", age: 10)
+        when:
+            String result = childPlayer.toString()
+        then:
+            !result.startsWith("${childPlayer.class.name}@")
+    }
+}

Groovy/ast/src/test/groovy/com/blogspot/toomuchcoding/util/SerializationUtil.groovy

+package com.blogspot.toomuchcoding.util
+
+class SerializationUtil {
+    static <T> T serializeAndBack(T obj) throws Exception {
+        ByteArrayOutputStream os = serializeObject(obj);
+        return (T) deserializeObject(os, Object.class);
+    }
+
+    static <T> T deserializeObject(ByteArrayOutputStream serialized, Class<T> type) throws IOException, ClassNotFoundException {
+        InputStream unserialize = new ByteArrayInputStream(serialized.toByteArray());
+        return deserializeObject(unserialize, type);
+    }
+
+    static <T> T deserializeObject(InputStream unserialize, Class<T> type) throws IOException, ClassNotFoundException {
+        return new ObjectInputStream(unserialize).readObject();
+    }
+
+    static ByteArrayOutputStream serializeObject(Object object) throws IOException {
+        ByteArrayOutputStream serialized = new ByteArrayOutputStream();
+        new ObjectOutputStream(serialized).writeObject(object);
+        return serialized;
+    }
+}