diff --git a/buildSrc b/buildSrc
deleted file mode 120000
index 82284049c054b0b6ea5abcbf181c7931a1400a63..0000000000000000000000000000000000000000
--- a/buildSrc
+++ /dev/null
@@ -1 +0,0 @@
-relast.preprocessor/buildSrc/
\ No newline at end of file
diff --git a/buildSrc/.gitignore b/buildSrc/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..567609b1234a9b8806c5a05da6c866e480aa148d
--- /dev/null
+++ b/buildSrc/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/buildSrc/src/main/java/org/jastadd/relast/plugin/CompilerLocationExtension.java b/buildSrc/src/main/java/org/jastadd/relast/plugin/CompilerLocationExtension.java
new file mode 100644
index 0000000000000000000000000000000000000000..7580e2dfa3277a07272a6b0adc94996da5db5ba6
--- /dev/null
+++ b/buildSrc/src/main/java/org/jastadd/relast/plugin/CompilerLocationExtension.java
@@ -0,0 +1,21 @@
+package org.jastadd.relast.plugin;
+
+import org.gradle.api.Project;
+import org.gradle.api.provider.Property;
+
+/**
+ * TODO: Add description.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class CompilerLocationExtension {
+  Property<String> compilerLocation;
+
+  public CompilerLocationExtension(Project project) {
+    compilerLocation = project.getObjects().property(String.class);
+  }
+
+  public Property<String> getCompilerLocation() {
+    return compilerLocation;
+  }
+}
diff --git a/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastPlugin.java b/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastPlugin.java
new file mode 100644
index 0000000000000000000000000000000000000000..1ce9e963b27c2efdf53092a91335680e708e7509
--- /dev/null
+++ b/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastPlugin.java
@@ -0,0 +1,39 @@
+package org.jastadd.relast.plugin;
+
+import org.gradle.api.Plugin;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.tasks.TaskCollection;
+
+import java.util.Set;
+
+/**
+ * Plugin for RelAst-Test.
+ *
+ * @author rschoene - Initial contribution
+ */
+public class RelastPlugin implements Plugin<Project> {
+
+  private Task testTask;
+
+  @Override
+  public void apply(Project project) {
+    CompilerLocationExtension extension = project.getExtensions().create(
+        "relastTest",
+        CompilerLocationExtension.class,
+        project);
+
+    Set<Task> tasks = project.getTasksByName("test", false);
+    // there should be only one task "test"
+    testTask = tasks.iterator().next();
+    TaskCollection<RelastTest> relastTests = project.getTasks().withType(RelastTest.class);
+    relastTests.forEach(relastTest -> setupRelastTest(relastTest, extension.getCompilerLocation().getOrNull()));
+    relastTests.whenTaskAdded(relastTest -> setupRelastTest(relastTest, extension.getCompilerLocation().getOrNull()));
+  }
+
+  private void setupRelastTest(RelastTest relastTest, String compilerLocation) {
+    testTask.dependsOn(relastTest);
+    relastTest.setCompilerLocation(compilerLocation);
+    relastTest.setGroup("verification");
+  }
+}
diff --git a/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastTest.java b/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6ee91a83b3ffa3cd59605afcc73cb9e7c473aac1
--- /dev/null
+++ b/buildSrc/src/main/java/org/jastadd/relast/plugin/RelastTest.java
@@ -0,0 +1,264 @@
+package org.jastadd.relast.plugin;
+
+import org.gradle.api.DefaultTask;
+import org.gradle.api.Project;
+import org.gradle.api.file.FileCollection;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.tasks.SourceSet;
+import org.gradle.api.tasks.SourceSetContainer;
+import org.gradle.api.tasks.TaskAction;
+
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * RelAst Test Task
+ *
+ * @author rschoene - Initial contribution
+ */
+@SuppressWarnings({"unused", "WeakerAccess"})
+public class RelastTest extends DefaultTask {
+  // configuration from plugin
+  private String compilerLocation;
+  // general options
+  private boolean verbose = false;
+  // pre-process options
+  private List<String> relastFiles = new ArrayList<>();
+  private boolean useJastAddNames;
+  private boolean resolverHelper;
+  private boolean writeToFile = true;
+  private String grammarName;
+  private String listClass;
+  private String jastAddList;
+  private String serializer;
+
+  // compile options
+  private boolean runJastAdd = true;
+  private String outputDir = "src/test/java-gen/";
+  private String packageName;
+  private final List<String> moreInputFiles = new ArrayList<>();
+
+  public void setCompilerLocation(String compilerLocation) {
+    this.compilerLocation = compilerLocation;
+  }
+
+  public boolean isVerbose() {
+    return verbose;
+  }
+
+  public void setVerbose(boolean verbose) {
+    this.verbose = verbose;
+  }
+
+  // pre-process options
+  public List<String> getRelastFiles() {
+    return relastFiles;
+  }
+
+  public void relastFiles(String relastFile) {
+    this.relastFiles.add(relastFile);
+  }
+
+  public void relastFiles(String[] relastFilesArray) {
+    this.relastFiles = Arrays.asList(relastFilesArray);
+  }
+
+  public boolean isUseJastAddNames() {
+    return useJastAddNames;
+  }
+
+  public void setUseJastAddNames(boolean useJastAddNames) {
+    this.useJastAddNames = useJastAddNames;
+  }
+
+  public boolean isResolverHelper() {
+    return resolverHelper;
+  }
+
+  public void setResolverHelper(boolean resolverHelper) {
+    this.resolverHelper = resolverHelper;
+  }
+
+  public boolean isWriteToFile() {
+    return writeToFile;
+  }
+
+  public void setWriteToFile(boolean writeToFile) {
+    this.writeToFile = writeToFile;
+  }
+
+  public String getGrammarName() {
+    return grammarName;
+  }
+
+  public void setGrammarName(String grammarName) {
+    this.grammarName = grammarName;
+  }
+
+  public String getListClass() {
+    return listClass;
+  }
+
+  public void setListClass(String listClass) {
+    this.listClass = listClass;
+  }
+
+  public String getJastAddList() {
+    return jastAddList;
+  }
+
+  public void setJastAddList(String jastAddList) {
+    this.jastAddList = jastAddList;
+  }
+
+  public String getSerializer() {
+    return serializer;
+  }
+
+  public void setSerializer(String serializer) {
+    this.serializer = serializer;
+  }
+
+  // compile options
+  public boolean isRunJastAdd() {
+    return runJastAdd;
+  }
+
+  public void setRunJastAdd(boolean runJastAdd) {
+    this.runJastAdd = runJastAdd;
+  }
+
+  public String getOutputDir() {
+    return outputDir;
+  }
+
+  public void setOutputDir(String outputDir) {
+    this.outputDir = outputDir;
+  }
+
+  public String getPackageName() {
+    return packageName;
+  }
+
+  public void setPackageName(String packageName) {
+    this.packageName = packageName;
+  }
+
+  public List<String> getMoreInputFiles() {
+    return moreInputFiles;
+  }
+
+  public void moreInputFiles(String f) {
+    this.moreInputFiles.add(f);
+  }
+
+  public void moreInputFiles(String[] fileArray) {
+    this.moreInputFiles.addAll(Arrays.asList(fileArray));
+  }
+
+  private boolean isSet(String option) {
+    return option != null && !option.isEmpty();
+  }
+
+  private String[] genSuffixes = {".ast", ".jadd", "RefResolver.jadd", "ResolverStubs.jrag", "Serializer.jadd"};
+
+  @TaskAction
+  void runTest() {
+    setGroup("verification");
+    setDescription("Runs a relast test");
+    Project project = getProject();
+    if (isVerbose()) {
+      System.out.println("Running relast test");
+      System.out.println("relast files: " + getRelastFiles());
+      System.out.println("Deleting files");
+    }
+    // first, delete generated files
+    List<String> genFiles = new ArrayList<>();
+    for (String suffix : genSuffixes) {
+      genFiles.add(getGrammarName() + suffix);
+    }
+    if (isVerbose()) {
+      System.out.println("gen files: " + genFiles);
+    }
+    project.delete(deleteSpec -> {
+      deleteSpec.delete(genFiles);
+      if (isSet(getPackageName())) {
+        deleteSpec.delete(Paths.get(getOutputDir(), getPackageName()));
+      }
+    });
+    if (isVerbose()) {
+      System.out.println("Pre processing, running relast");
+    }
+    // then, run relast pre processing
+    project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
+      SourceSetContainer sourceSets = (SourceSetContainer) project.getProperties().get("sourceSets");
+      FileCollection runtimeClasspath = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath();
+      project.javaexec(javaExecSpec -> {
+        List<Object> args = new ArrayList<>();
+        javaExecSpec.setClasspath(runtimeClasspath);
+        if (compilerLocation != null) {
+          javaExecSpec.setMain("-jar");
+          args.add(compilerLocation);
+        } else {
+          javaExecSpec.setMain("org.jastadd.relast.compiler.Compiler");
+        }
+        args.addAll(getRelastFiles());
+        args.add("--quiet");
+        if (isWriteToFile()) {
+          args.add("--file");
+        }
+        if (isUseJastAddNames()) {
+          args.add("--useJastAddNames");
+        }
+        if (isResolverHelper()) {
+          args.add("--resolverHelper");
+        }
+        if (isSet(getJastAddList())) {
+          args.add("--jastAddList=" + getJastAddList());
+        }
+        if (isSet(getListClass())) {
+          args.add("--listClass=" + getListClass());
+        }
+        if (isSet(getSerializer())) {
+          args.add("--serializer=" + getSerializer());
+        }
+        args.add("--grammarName=" + getGrammarName());
+        if (isVerbose()) {
+          System.out.println("Start relast with args: " + args);
+        }
+        javaExecSpec.args(args);
+      });
+    });
+    if (isRunJastAdd()) {
+      if (isVerbose()) {
+        System.out.println("Compile with JastAdd");
+      }
+      // check which files were actually generated
+      genFiles.removeIf(s -> !Paths.get(s).toFile().exists());
+      // finally, compile generated files
+      project.getPlugins().withType(JavaPlugin.class, javaPlugin -> {
+        SourceSetContainer sourceSets = (SourceSetContainer) project.getProperties().get("sourceSets");
+        FileCollection runtimeClasspath = sourceSets.getByName(SourceSet.MAIN_SOURCE_SET_NAME).getRuntimeClasspath();
+        project.javaexec(javaExecSpec -> {
+          javaExecSpec.setClasspath(runtimeClasspath);
+          javaExecSpec.setMain("org.jastadd.JastAdd");
+          List<Object> args = new ArrayList<>();
+          args.add("--o=" + getOutputDir());
+          args.add("--package=" + getPackageName());
+          if (isSet(getJastAddList())) {
+            args.add("--List=" + getJastAddList());
+          }
+          args.addAll(genFiles);
+          args.addAll(getMoreInputFiles());
+          if (isVerbose()) {
+            System.out.println("Start JastAdd with args: " + args);
+          }
+          javaExecSpec.args(args);
+        });
+      });
+    }
+  }
+
+}
diff --git a/buildSrc/src/main/resources/META-INF/gradle-plugins/org.jastadd.relast.plugin.properties b/buildSrc/src/main/resources/META-INF/gradle-plugins/org.jastadd.relast.plugin.properties
new file mode 100644
index 0000000000000000000000000000000000000000..20222b8953522469ec2c0340ea3aeb5ed6294d13
--- /dev/null
+++ b/buildSrc/src/main/resources/META-INF/gradle-plugins/org.jastadd.relast.plugin.properties
@@ -0,0 +1 @@
+implementation-class=org.jastadd.relast.plugin.RelastPlugin
diff --git a/ros2rag.base/src/main/jastadd/Ros2Rag.relast b/ros2rag.base/src/main/jastadd/Ros2Rag.relast
index 166950375f5e56e0ade642999d02cd35d46e61fe..1008207a6ceb854a1937e7a2a16ef6e22cbccc03 100644
--- a/ros2rag.base/src/main/jastadd/Ros2Rag.relast
+++ b/ros2rag.base/src/main/jastadd/Ros2Rag.relast
@@ -11,11 +11,12 @@ ReadFromMqttDefinition : TokenUpdateDefinition;
 WriteToMqttDefinition  : TokenUpdateDefinition;
 
 // example: RobotArm._AppropriateSpeed canDependOn Joint.CurrentPosition as dependency1
-DependencyDefinition ::= <ID> ;
-rel DependencyDefinition.Source -> TokenComponent ;
-rel DependencyDefinition.Target -> TokenComponent ;
+DependencyDefinition ::= <ID>;
+rel DependencyDefinition.Source <-> TokenComponent.DependencySourceDefinition*;
+rel DependencyDefinition.Target -> TokenComponent;
 
 MappingDefinition ::= <ID> FromType:MappingDefinitionType <FromVariableName> ToType:MappingDefinitionType <Content> ;
 abstract MappingDefinitionType ::= ;
 JavaMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
 JavaArrayMappingDefinitionType : MappingDefinitionType ::= Type:JavaTypeUse ;
+DefaultMappingDefinition : MappingDefinition ;
diff --git a/ros2rag.base/src/main/jastadd/backend/Generation.jadd b/ros2rag.base/src/main/jastadd/backend/Generation.jadd
index c5f253f4a5a2c2d1d289512bd003927233eb790c..75be20c251f171af965eda685e00e489e3de023d 100644
--- a/ros2rag.base/src/main/jastadd/backend/Generation.jadd
+++ b/ros2rag.base/src/main/jastadd/backend/Generation.jadd
@@ -11,6 +11,8 @@ aspect GenerationUtils {
 }
 
 aspect AspectGeneration {
+  syn String TokenComponent.internalName() = getDependencySourceDefinitionList().isEmpty() ? externalName() : "_internal_" + getName();
+  syn String TokenComponent.externalName() = getName();
 
   // naming convention attributes
   syn String TokenUpdateDefinition.connectMethod() = "connect" + getToken().getName();
@@ -24,7 +26,7 @@ aspect AspectGeneration {
     Character.toUpperCase(getID().charAt(0)) +
     getID().substring(1);
   syn String DependencyDefinition.internalRelationPrefix() = "_internal_" + getID();
-  syn String DependencyDefinition.internalTokenName() = "_internal" + getSource().getName();
+  syn String DependencyDefinition.internalTokenName() = getSource().internalName();
 
   inh String UpdateDefinition.mqttUpdaterAttribute();
   inh String MappingDefinition.mqttUpdaterAttribute();
@@ -66,7 +68,7 @@ aspect AspectGeneration {
     sb.append(ind(1)).append("}\n\n");
 
     // mqttWaitUntilReady
-    sb.append(ind(1)).append("public void ").append(rootNodeName).append(".")
+    sb.append(ind(1)).append("public boolean ").append(rootNodeName).append(".")
       .append(mqttWaitUntilReadyMethod()).append("(long time, java.util.concurrent.TimeUnit unit) {\n");
     sb.append(ind(2)).append("return ").append(mqttUpdaterField()).append(".waitUntilReady(time, unit);\n");
     sb.append(ind(1)).append("}\n\n");
@@ -111,20 +113,32 @@ aspect AspectGeneration {
       String initialInputVariableName) {
     final String resultVariablePrefix = "result";  // we do not need "_" here, because methodName begins with one
     String inputVariableName = initialInputVariableName;
+    // last variable need to be declared before begin of try
+    MappingDefinition lastDefinition = getMappingList().get(getMappingList().size() - 1);
+    sb.append(ind(indent)).append(lastDefinition.getToType().prettyPrint()).append(" ")
+      .append(resultVariablePrefix).append(lastDefinition.methodName()).append(";\n");
+    sb.append(ind(indent)).append("try {\n");
     for (MappingDefinition mappingDefinition : getMappingList()) {
       String resultVariableName = resultVariablePrefix + mappingDefinition.methodName();
-      sb.append(ind(indent)).append(mappingDefinition.getToType().prettyPrint()).append(" ")
-        .append(resultVariablePrefix).append(mappingDefinition.methodName())
+      sb.append(ind(indent + 1));
+      if (mappingDefinition != lastDefinition) {
+        sb.append(mappingDefinition.getToType().prettyPrint()).append(" ");
+      }
+      sb.append(resultVariablePrefix).append(mappingDefinition.methodName())
         .append(" = ").append(mappingDefinition.methodName()).append("(")
         .append(inputVariableName).append(");\n");
       inputVariableName = resultVariableName;
     }
+    sb.append(ind(indent)).append("} catch (Exception e) {\n");
+    sb.append(ind(indent + 1)).append("e.printStackTrace();\n");
+    sb.append(ind(indent + 1)).append(preemptiveReturnStatement()).append("\n");
+    sb.append(ind(indent)).append("}\n");
     if (!getAlwaysApply()) {
-      sb.append(ind(indent)).append("if (get").append(getToken().getName()).append("()");
+      sb.append(ind(indent)).append("if (").append(preemptiveExpectedValue());
       if (getToken().isPrimitiveType()) {
         sb.append(" == ").append(inputVariableName);
       } else {
-        sb.append(" != null ? get").append(getToken().getName()).append("().equals(")
+        sb.append(" != null ? ").append(preemptiveExpectedValue()).append(".equals(")
           .append(inputVariableName).append(")").append(" : ")
           .append(inputVariableName).append(" == null");
       }
@@ -133,6 +147,10 @@ aspect AspectGeneration {
     return inputVariableName;
   }
 
+  syn String TokenUpdateDefinition.preemptiveExpectedValue();
+  eq ReadFromMqttDefinition.preemptiveExpectedValue() = "get" + getToken().getName() + "()";
+  eq WriteToMqttDefinition.preemptiveExpectedValue() = lastValue();
+
   syn String TokenUpdateDefinition.preemptiveReturnStatement();
   eq ReadFromMqttDefinition.preemptiveReturnStatement() = "return;";
   eq WriteToMqttDefinition.preemptiveReturnStatement() = "return false;";
@@ -188,7 +206,8 @@ aspect AspectGeneration {
   void MappingDefinition.generateAspect(StringBuilder sb) {
     sb.append(ind(1)).append("protected static ").append(getToType().prettyPrint())
       .append(" ASTNode.").append(methodName()).append("(")
-      .append(getFromType().prettyPrint()).append(" ").append(getFromVariableName()).append(") {\n");
+      .append(getFromType().prettyPrint()).append(" ").append(getFromVariableName())
+      .append(") throws Exception {\n");
     for (String line : getContent().split("\n")) {
       if (!line.trim().isEmpty()) {
         sb.append(ind(2)).append(line).append("\n");
@@ -226,7 +245,45 @@ aspect AspectGeneration {
     sb.append(ind(1)).append("public ");
     getSource().getJavaTypeUse().generateAbstractGrammar(sb);
     sb.append(" ").append(sourceParentTypeName).append(".get").append(getSource().getName()).append("() {\n");
-    sb.append(ind(2)).append("return get").append(internalTokenName()).append(";\n");
+    sb.append(ind(2)).append("return get").append(internalTokenName()).append("();\n");
     sb.append(ind(1)).append("}\n\n");
   }
 }
+
+aspect RelationGeneration {
+  syn java.util.List<Relation> Ros2Rag.additionalRelations() {
+    java.util.List<Relation> result = new java.util.ArrayList<>();
+    for (DependencyDefinition dd : getDependencyDefinitionList()) {
+      result.add(dd.getRelationToCreate());
+    }
+    return result;
+  }
+
+  syn nta Relation DependencyDefinition.getRelationToCreate() {
+    BidirectionalRelation result = new BidirectionalRelation();
+    NavigableRole left = new ListRole(internalRelationPrefix() + "Source");
+    left.setType(getTarget().containingTypeDecl());
+    NavigableRole right = new ListRole(internalRelationPrefix() + "Target");
+    right.setType(getSource().containingTypeDecl());
+    result.setLeft(left);
+    result.setRight(right);
+    return result;
+  }
+}
+
+aspect GrammarExtension {
+  refine BackendAbstractGrammar public void TokenComponent.generateAbstractGrammar(StringBuilder b) {
+    if (getNTA()) {
+      b.append("/");
+    }
+    b.append("<");
+    if (!getName().equals("")) {
+      b.append(internalName()).append(":");
+    }
+    getJavaTypeUse().generateAbstractGrammar(b);
+    b.append(">");
+    if (getNTA()) {
+      b.append("/");
+    }
+  }
+}
diff --git a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java
index a4f5bdc7f53251fecbd0aaf86d98faf017684259..7cd97a36113ca9af2566321f77b59e929f70b0cd 100644
--- a/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java
+++ b/ros2rag.base/src/main/java/org/jastadd/ros2rag/compiler/Compiler.java
@@ -139,11 +139,12 @@ public class Compiler {
   private Ros2Rag parseProgram(String inputGrammarFileName, String inputRos2RagFileName) throws CompilerException {
     Program program = new Program();
     Ros2Rag ros2Rag;
+    GrammarFile inputGrammar;
 
     try (BufferedReader reader = Files.newBufferedReader(Paths.get(inputGrammarFileName))) {
       Ros2RagScanner scanner = new Ros2RagScanner(reader);
       Ros2RagParser parser = new Ros2RagParser();
-      GrammarFile inputGrammar = (GrammarFile) parser.parse(scanner);
+      inputGrammar = (GrammarFile) parser.parse(scanner);
       inputGrammar.dumpTree(System.out);
       program.addGrammarFile(inputGrammar);
       inputGrammar.treeResolveAll();
@@ -159,11 +160,8 @@ public class Compiler {
     } catch (IOException | Parser.Exception e) {
       throw new CompilerException("Could not parse ros2rag file " + inputRos2RagFileName, e);
     }
-
-    ros2Rag.dumpTree(System.out);
     ros2Rag.treeResolveAll();
-    ros2Rag.dumpTree(System.out);
-
+    ros2Rag.additionalRelations().forEach(inputGrammar::addDeclaration);
     return ros2Rag;
   }
 
diff --git a/ros2rag.base/src/main/resources/MqttUpdater.jadd b/ros2rag.base/src/main/resources/MqttUpdater.jadd
index 53fa6cf5dacd874382416d2855eca9cbef225cd5..ef3e483fc80a389f4d5311cbfc6b9c0dccab7885 100644
--- a/ros2rag.base/src/main/resources/MqttUpdater.jadd
+++ b/ros2rag.base/src/main/resources/MqttUpdater.jadd
@@ -144,7 +144,7 @@ public class MqttUpdater {
     callbacks.put(topic, callback);
 
     // subscribe at broker
-    Topic[] topicArray = { new Topic(topic, this.qos) };
+    org.fusesource.mqtt.client.Topic[] topicArray = { new org.fusesource.mqtt.client.Topic(topic, this.qos) };
     connection.subscribe(topicArray, new org.fusesource.mqtt.client.Callback<byte[]>() {
       @Override
       public void onSuccess(byte[] qoses) {
diff --git a/ros2rag.tests/.gitignore b/ros2rag.tests/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..87b4cdd3d7c6a41502ca98703abeeb69a1d536fb
--- /dev/null
+++ b/ros2rag.tests/.gitignore
@@ -0,0 +1,5 @@
+build
+src/gen-res/
+src/gen/
+out/
+*.class
diff --git a/ros2rag.tests/build.gradle b/ros2rag.tests/build.gradle
index bedeb8ce63ceaaa9ebe48fbd7e8c7448a406b262..f5cf3a7c17cf525412866197429bcdcbffe963ec 100644
--- a/ros2rag.tests/build.gradle
+++ b/ros2rag.tests/build.gradle
@@ -1,7 +1,7 @@
 import org.jastadd.relast.plugin.RelastPlugin
 import org.jastadd.relast.plugin.RelastTest
 apply plugin: 'jastadd'
-apply plugin: 'application'
+apply plugin: 'com.google.protobuf'
 apply plugin: RelastPlugin
 
 sourceCompatibility = 1.8
@@ -14,6 +14,7 @@ buildscript {
     repositories.jcenter()
     dependencies {
         classpath 'org.jastadd:jastaddgradle:1.13.3'
+        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
     }
 }
 
@@ -44,6 +45,15 @@ sourceSets {
     }
 }
 
+protobuf {
+    // create strange directories, so use default here
+    generatedFilesBaseDir = "$projectDir/src/test/java-gen/proto"
+    protoc {
+        // The artifact spec for the Protobuf Compiler
+        artifact = 'com.google.protobuf:protoc:3.0.0'
+    }
+}
+
 task preprocessExampleTest(type: JavaExec, group: 'verification') {
     doFirst {
         delete 'src/test/02-after-ros2rag/example/Grammar.relast',
@@ -77,7 +87,7 @@ task preprocessExampleTest(type: JavaExec, group: 'verification') {
 //}
 
 task compileExampleTest(type: RelastTest) {
-    verbose = true
+    useJastAddNames = true
     relastFiles 'src/test/02-after-ros2rag/example/Grammar.relast'
     grammarName = 'src/test/03-after-relast/example/example'
     packageName = 'example.ast'
diff --git a/ros2rag.tests/src/test/01-input/example/Example.jadd b/ros2rag.tests/src/test/01-input/example/Example.jadd
index 1fd25492334984294171edab4f2e2ffae89ddc9c..40ae586a2a0feb5c4fe274cc71ba5f05da4cbaad 100644
--- a/ros2rag.tests/src/test/01-input/example/Example.jadd
+++ b/ros2rag.tests/src/test/01-input/example/Example.jadd
@@ -44,4 +44,42 @@ aspect GrammarTypes {
       return "(" + x + ", " + y + ", " + z + ")";
     }
   }
+
+  inh Model RobotArm.model();
+  eq Model.getRobotArm().model() = this;
+
+  inh RobotArm Joint.containingRobotArm();
+  eq RobotArm.getJoint().containingRobotArm() = this;
+  eq RobotArm.getEndEffector().containingRobotArm() = this;
+
+  syn boolean RobotArm.isInSafetyZone() {
+    for (Joint joint : getJointList()) {
+      if (model().getZoneModel().isInSafetyZone(joint.getCurrentPosition())) {
+        return true;
+      }
+    }
+    return model().getZoneModel().isInSafetyZone(getEndEffector().getCurrentPosition());
+  }
+
+  cache ZoneModel.isInSafetyZone(IntPosition pos);
+  syn boolean ZoneModel.isInSafetyZone(IntPosition pos) {
+    for (Zone sz : getSafetyZoneList()) {
+      for (Coordinate coordinate : sz.getCoordinateList()) {
+        IntPosition inside = coordinate.getPosition();
+        if (inside.getX() <= pos.getX() &&
+              inside.getY() <= pos.getY() &&
+              inside.getZ() <= pos.getZ() &&
+              pos.getX() <= inside.getX() + getSize().getX() &&
+              pos.getY() <= inside.getY() + getSize().getY() &&
+              pos.getZ() <= inside.getZ() + getSize().getZ()) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  syn double RobotArm.getAppropriateSpeed() {
+    return isInSafetyZone() ? 0.4d : 1.0d;
+  }
 }
diff --git a/ros2rag.tests/src/test/01-input/example/Example.relast b/ros2rag.tests/src/test/01-input/example/Example.relast
index 5afeb2250283d049143a0134247fb1e374ac4296..c6b346d5df0db5b333141202a800b5951643eef2 100644
--- a/ros2rag.tests/src/test/01-input/example/Example.relast
+++ b/ros2rag.tests/src/test/01-input/example/Example.relast
@@ -4,9 +4,9 @@ ZoneModel ::= <Size:IntPosition> SafetyZone:Zone* ;
 
 Zone ::= Coordinate* ;
 
-RobotArm ::= Joint* EndEffector <AttributeTestSource:int> /<AppropriateSpeed:double>/ ; // normally this would be: <AttributeTestSource:int> ;
+RobotArm ::= Joint* EndEffector <AttributeTestSource:int> /<AppropriateSpeed:double>/ ;
 
-Joint ::= <Name:String> <CurrentPosition:IntPosition> ;  // normally this would be: <CurrentPosition:IntPosition>
+Joint ::= <Name:String> <CurrentPosition:IntPosition> ;
 
 EndEffector : Joint;
 
diff --git a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
index 7b41810c351943775074c5fe1a0abd9e47a5afc6..9ff94b7b15bbbce1a2d2936abd53921c7b140591 100644
--- a/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
+++ b/ros2rag.tests/src/test/java/org/jastadd/ros2rag/tests/ExampleTest.java
@@ -3,6 +3,8 @@ package org.jastadd.ros2rag.tests;
 import example.ast.*;
 import org.junit.jupiter.api.Test;
 
+import java.io.IOException;
+
 /**
  * Test case "example".
  *
@@ -11,7 +13,7 @@ import org.junit.jupiter.api.Test;
 public class ExampleTest {
 
   @Test
-  public void buildModel() {
+  public void buildModel() throws IOException {
     Model model = new Model();
     model.MqttSetHost("localhost");
 
diff --git a/ros2rag.tests/src/test/01-input/example/linkstate.proto b/ros2rag.tests/src/test/proto/linkstate.proto
similarity index 100%
rename from ros2rag.tests/src/test/01-input/example/linkstate.proto
rename to ros2rag.tests/src/test/proto/linkstate.proto
diff --git a/ros2rag.tests/src/test/01-input/example/robotconfig.proto b/ros2rag.tests/src/test/proto/robotconfig.proto
similarity index 100%
rename from ros2rag.tests/src/test/01-input/example/robotconfig.proto
rename to ros2rag.tests/src/test/proto/robotconfig.proto