From bb92be214683ddd6dc7090b89ebc8c224c5ccd76 Mon Sep 17 00:00:00 2001
From: Johannes Mey <johannes.mey@tu-dresden.de>
Date: Mon, 25 Feb 2019 13:58:35 +0100
Subject: [PATCH] move more tests to gradle junit5 tests

---
 .gitignore                                    |   4 +
 build.gradle                                  |  66 +-
 {tests/valid => src/test/jastadd}/Utils.jadd  |   0
 .../jastadd/lowerbounds}/LowerBounds.relast   |   0
 .../test/jastadd/relations/Relations.relast   |   0
 src/test/jastadd/relations/Relations2.ast     |   3 +
 src/test/jastadd/relations/Relations2.jadd    |  43 ++
 .../jastadd/relast/tests/AnalysisTests.java   |   7 -
 .../relast/tests/{errors => }/Errors.java     |  38 +-
 .../org/jastadd/relast/tests/LowerBounds.java |  51 ++
 .../org/jastadd/relast/tests/Relations.java   | 635 ++++++++++++++++++
 .../org/jastadd/relast/tests/TestHelpers.java |  40 ++
 .../relast/tests/testsuite/TestSuite.java     | 226 -------
 tests/multipleFiles/.gitignore                |   4 +-
 tests/multipleFiles/All.ast                   |   3 +
 tests/multipleFiles/All.jadd                  | 560 +++++++++++++++
 tests/multipleFiles/Makefile                  |  12 +-
 tests/resolverHelper/.gitignore               |   4 +-
 tests/resolverHelper/All.ast                  |   9 +
 tests/resolverHelper/All.jadd                 |  83 +++
 tests/resolverHelper/Makefile                 |   8 +-
 tests/valid/.gitignore                        |   3 -
 tests/valid/AbstractTests.java                |  25 -
 tests/valid/All.java                          | 629 -----------------
 tests/valid/LowerBounds.java                  |  47 --
 tests/valid/Makefile                          |  22 -
 26 files changed, 1515 insertions(+), 1007 deletions(-)
 rename {tests/valid => src/test/jastadd}/Utils.jadd (100%)
 rename {tests/valid => src/test/jastadd/lowerbounds}/LowerBounds.relast (100%)
 rename tests/valid/All.relast => src/test/jastadd/relations/Relations.relast (100%)
 create mode 100644 src/test/jastadd/relations/Relations2.ast
 create mode 100644 src/test/jastadd/relations/Relations2.jadd
 delete mode 100644 src/test/java/org/jastadd/relast/tests/AnalysisTests.java
 rename src/test/java/org/jastadd/relast/tests/{errors => }/Errors.java (65%)
 create mode 100644 src/test/java/org/jastadd/relast/tests/LowerBounds.java
 create mode 100644 src/test/java/org/jastadd/relast/tests/Relations.java
 create mode 100644 src/test/java/org/jastadd/relast/tests/TestHelpers.java
 delete mode 100644 src/test/java/org/jastadd/relast/tests/testsuite/TestSuite.java
 create mode 100644 tests/multipleFiles/All.ast
 create mode 100644 tests/multipleFiles/All.jadd
 create mode 100644 tests/resolverHelper/All.ast
 create mode 100644 tests/resolverHelper/All.jadd
 delete mode 100644 tests/valid/.gitignore
 delete mode 100644 tests/valid/AbstractTests.java
 delete mode 100644 tests/valid/All.java
 delete mode 100644 tests/valid/LowerBounds.java
 delete mode 100644 tests/valid/Makefile

diff --git a/.gitignore b/.gitignore
index ed82bf2..991c176 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,7 @@ build
 src/gen-res/
 src/gen/
 *.class
+src/test/jastadd/relations/Relations.ast
+src/test/jastadd/relations/Relations.jadd
+src/test/jastadd/lowerbounds/LowerBounds.ast
+src/test/jastadd/lowerbounds/LowerBounds.jadd
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 3151c06..a8176e2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -21,6 +21,7 @@ buildscript {
 dependencies {
     testImplementation 'org.junit.jupiter:junit-jupiter-api:5.4.0'
     testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.4.0'
+    runtime 'org.jastadd:jastadd:2.3.2'
     compile group: 'net.sf.beaver', name: 'beaver-rt', version: '0.9.11'
     compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.10.0'
     compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.10.0'
@@ -30,6 +31,9 @@ sourceSets {
     main {
         java.srcDir "src/gen/java"
     }
+    test {
+        java.srcDir "src/test/java-gen"
+    }
 }
 
 jar {
@@ -98,8 +102,68 @@ jastadd {
     jastaddOptions = ["--lineColumnNumbers", "--safeLazy", "--visitCheck=true", "--rewrite=cnta", "--cache=all"]
 }
 
+task preprocessRelationTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/jastadd/relations/Relations.ast', 'src/test/jastadd/relations/Relations.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.relast.compiler.Compiler'
+    args 'src/test/jastadd/relations/Relations.relast', '--file', '--grammarName=src/test/jastadd/relations/Relations'
+}
+
+task doublePreprocessRelationTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/jastadd/relations/Relations2.ast', 'src/test/jastadd/relations/Relations2.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.relast.compiler.Compiler'
+    args 'src/test/jastadd/relations/Relations.ast', '--file', '--grammarName=src/test/jastadd/relations/Relations2'
+}
+
+task compileRelationTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/java-gen/relations'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.JastAdd'
+    args '--o=src/test/java-gen/', '--package=relations.ast', 'src/test/jastadd/relations/Relations.ast', 'src/test/jastadd/relations/Relations.jadd', 'src/test/jastadd/Utils.jadd'
+}
+
+task preprocessLowerBoundsTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/jastadd/lowerbounds/LowerBounds.ast', 'src/test/jastadd/lowerbounds/LowerBounds.jadd'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.relast.compiler.Compiler'
+    args 'src/test/jastadd/lowerbounds/LowerBounds.relast', '--file', '--grammarName=src/test/jastadd/lowerbounds/LowerBounds'
+}
+
+task compileLowerBoundsTest(type: JavaExec, group: 'verification') {
+
+    doFirst {
+        delete 'src/test/java-gen/lowerbounds'
+    }
+
+    classpath = sourceSets.main.runtimeClasspath
+    main = 'org.jastadd.JastAdd'
+    args '--o=src/test/java-gen/', '--package=lowerbounds.ast', 'src/test/jastadd/lowerbounds/LowerBounds.ast', 'src/test/jastadd/lowerbounds/LowerBounds.jadd', 'src/test/jastadd/Utils.jadd'
+}
 
 test {
     outputs.upToDateWhen { false }
     useJUnitPlatform()
-}
\ No newline at end of file
+}
+
+test.dependsOn compileRelationTest
+test.dependsOn compileLowerBoundsTest
+doublePreprocessRelationTest.dependsOn preprocessRelationTest
+compileRelationTest.dependsOn doublePreprocessRelationTest
+compileLowerBoundsTest.dependsOn preprocessLowerBoundsTest
\ No newline at end of file
diff --git a/tests/valid/Utils.jadd b/src/test/jastadd/Utils.jadd
similarity index 100%
rename from tests/valid/Utils.jadd
rename to src/test/jastadd/Utils.jadd
diff --git a/tests/valid/LowerBounds.relast b/src/test/jastadd/lowerbounds/LowerBounds.relast
similarity index 100%
rename from tests/valid/LowerBounds.relast
rename to src/test/jastadd/lowerbounds/LowerBounds.relast
diff --git a/tests/valid/All.relast b/src/test/jastadd/relations/Relations.relast
similarity index 100%
rename from tests/valid/All.relast
rename to src/test/jastadd/relations/Relations.relast
diff --git a/src/test/jastadd/relations/Relations2.ast b/src/test/jastadd/relations/Relations2.ast
new file mode 100644
index 0000000..4bd2e3a
--- /dev/null
+++ b/src/test/jastadd/relations/Relations2.ast
@@ -0,0 +1,3 @@
+Root ::= A* B*;
+A ::= <Name:String> <_impl_Di2:B> <_impl_Bi2:B> <_impl_Bi4:B> <_impl_Bi3:B> <_impl_Bi8:ArrayList<B>> <_impl_Bi9:ArrayList<B>> <_impl_Bi1:B> <_impl_Bi5:B> <_impl_Di3:ArrayList<B>> <_impl_Bi7:ArrayList<B>> <_impl_Bi6:B> <_impl_Di1:B>;
+B ::= <Name:String> <_impl_Bi7:A> <_impl_Bi3:ArrayList<A>> <_impl_Bi5:A> <_impl_Bi1:A> <_impl_Bi6:ArrayList<A>> <_impl_Bi9:ArrayList<A>> <_impl_Bi2:A> <_impl_Bi4:A> <_impl_Bi8:A>;
diff --git a/src/test/jastadd/relations/Relations2.jadd b/src/test/jastadd/relations/Relations2.jadd
new file mode 100644
index 0000000..3fed2f9
--- /dev/null
+++ b/src/test/jastadd/relations/Relations2.jadd
@@ -0,0 +1,43 @@
+import java.util.ArrayList;
+import java.util.Collections;
+aspect RelAstAPI {
+  public boolean ASTNode.violatesLowerBounds() {
+    return !getLowerBoundsViolations().isEmpty();
+  }
+  public java.util.List<Pair<ASTNode, String>> ASTNode.getLowerBoundsViolations() {
+    ArrayList<Pair<ASTNode, String>> list = new ArrayList<>();
+    computeLowerBoundsViolations(list);
+    return list;
+  }
+  public void ASTNode.computeLowerBoundsViolations(java.util.List<Pair<ASTNode, String>> list) {
+    for (int i = 0; i < getNumChildNoTransform(); i++) {
+      getChildNoTransform(i).computeLowerBoundsViolations(list);
+    }
+  }
+  public class Pair<T1, T2> {
+    public final T1 _1;
+    public final T2 _2;
+    public Pair(T1 _1, T2 _2) {
+      ASTNode.assertNotNull(_1);
+      ASTNode.assertNotNull(_2);
+      this._1 = _1;
+      this._2 = _2;
+    }
+    public boolean equals(Object other) {
+      if (other instanceof Pair) {
+        Pair<?,?> p = (Pair<?,?>) other;
+        return _1.equals(p._1) && _2.equals(p._2);
+      } else {
+        return false;
+      }
+    }
+    public int hashCode() {
+      return 31*_1.hashCode() + _2.hashCode();
+    }
+  }
+  public static void ASTNode.assertNotNull(Object obj) {
+    if (obj == null) {
+      throw new NullPointerException();
+    }
+  }
+}
diff --git a/src/test/java/org/jastadd/relast/tests/AnalysisTests.java b/src/test/java/org/jastadd/relast/tests/AnalysisTests.java
deleted file mode 100644
index ec19c2c..0000000
--- a/src/test/java/org/jastadd/relast/tests/AnalysisTests.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.jastadd.relast.tests;
-
-import org.jastadd.relast.tests.testsuite.TestSuite;
-
-public class AnalysisTests extends TestSuite {
-
-}
diff --git a/src/test/java/org/jastadd/relast/tests/errors/Errors.java b/src/test/java/org/jastadd/relast/tests/Errors.java
similarity index 65%
rename from src/test/java/org/jastadd/relast/tests/errors/Errors.java
rename to src/test/java/org/jastadd/relast/tests/Errors.java
index 67d005a..63b597f 100644
--- a/src/test/java/org/jastadd/relast/tests/errors/Errors.java
+++ b/src/test/java/org/jastadd/relast/tests/Errors.java
@@ -1,4 +1,4 @@
-package org.jastadd.relast.tests.errors;
+package org.jastadd.relast.tests;
 
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
@@ -9,42 +9,14 @@ import org.junit.jupiter.api.Test;
 import java.io.File;
 import java.io.IOException;
 import java.nio.charset.Charset;
-import java.nio.file.Files;
-import java.nio.file.Paths;
 
+import static org.jastadd.relast.tests.TestHelpers.exec;
+import static org.jastadd.relast.tests.TestHelpers.readFile;
 
-public class Errors {
 
-  private static final Logger logger = LogManager.getLogger(Errors.class);
-
-  static String readFile(String path, Charset encoding)
-      throws IOException {
-    byte[] encoded = Files.readAllBytes(Paths.get(path));
-    return new String(encoded, encoding);
-  }
-
-  static int exec(Class klass, String[] args, File err) throws IOException,
-      InterruptedException {
-    String javaHome = System.getProperty("java.home");
-    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
-    String classpath = System.getProperty("java.class.path");
-    String className = klass.getName();
-
-    String[] newArgs = new String[args.length + 4];
-    newArgs[0] = javaBin;
-    newArgs[1] = "-cp";
-    newArgs[2] = classpath;
-    newArgs[3] = className;
-    System.arraycopy(args, 0, newArgs, 4, args.length);
-
-    ProcessBuilder builder = new ProcessBuilder(newArgs);
-    builder.redirectError(err);
-
-    Process process = builder.start();
-    process.waitFor();
-    return process.exitValue();
-  }
+class Errors {
 
+  private static final Logger logger = LogManager.getLogger(Errors.class);
   @Test
   void test1() throws IOException {
 
diff --git a/src/test/java/org/jastadd/relast/tests/LowerBounds.java b/src/test/java/org/jastadd/relast/tests/LowerBounds.java
new file mode 100644
index 0000000..ff20a2b
--- /dev/null
+++ b/src/test/java/org/jastadd/relast/tests/LowerBounds.java
@@ -0,0 +1,51 @@
+package org.jastadd.relast.tests;
+
+import lowerbounds.ast.*;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class LowerBounds {
+
+  /*
+   * Root ::= A* B*;
+   * A ::= <Name> [C];
+   * B ::= <Name>;
+   * C ::= <Name>;
+   * rel A.Br -> B;
+   * rel B.Cr <-> C.Br;
+   * rel Root.Aa? -> A;
+   */
+  @Test
+  void test() {
+    Root r = new Root();
+    C c1 = new C("c1");
+    C c2 = new C("c2");
+    A a1 = new A("a1", new Opt<>(c1));
+    A a2 = new A("a2", new Opt<>(c2));
+    B b1 = new B("b1");
+    B b2 = new B("b2");
+    r.addA(a1);
+    r.addA(a2);
+    r.addB(b1);
+    r.addB(b2);
+
+    assertTrue(r.violatesLowerBounds());
+
+    a1.setBr(b1);
+    a2.setBr(b2);
+    b1.setCr(c1);
+    b2.setCr(c2);
+
+    assertFalse(r.violatesLowerBounds());
+
+    b2.setCr(c1);
+
+    assertTrue(r.violatesLowerBounds());
+
+    b1.setCr(c2);
+
+    assertFalse(r.violatesLowerBounds());
+  }
+}
diff --git a/src/test/java/org/jastadd/relast/tests/Relations.java b/src/test/java/org/jastadd/relast/tests/Relations.java
new file mode 100644
index 0000000..a23d998
--- /dev/null
+++ b/src/test/java/org/jastadd/relast/tests/Relations.java
@@ -0,0 +1,635 @@
+package org.jastadd.relast.tests;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import relations.ast.A;
+import relations.ast.B;
+import relations.ast.Root;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import static org.jastadd.relast.tests.TestHelpers.readFile;
+import static org.junit.jupiter.api.Assertions.*;
+
+
+@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
+class Relations {
+  private Root r;
+  private A a1;
+  private A a2;
+  private A a3;
+  private B b1;
+  private B b2;
+  private B b3;
+
+  @Test
+  void doubleRelastRun() throws IOException {
+
+    String firstRun = "./src/test/jastadd/relations/Relations.ast";
+    String secondRun = "./src/test/jastadd/relations/Relations2.ast";
+
+
+    String first = readFile(firstRun, Charset.defaultCharset());
+    String second = readFile(secondRun, Charset.defaultCharset());
+
+    Assertions.assertEquals(first, second);
+  }
+
+  /**
+   * rel A.Di1 -> B;
+   */
+  @Test
+  void testDi1() {
+    setup();
+    a1.setDi1(b2);
+    a2.setDi1(b1);
+
+    assertSame(a1.getDi1(), b2);
+    assertSame(a2.getDi1(), b1);
+
+    a2.setDi1(b2);
+
+    assertSame(a1.getDi1(), b2);
+    assertSame(a2.getDi1(), b2);
+
+    try {
+      a3.setDi1(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Di2? -> B;
+   */
+  @Test
+  void testDi2() {
+    setup();
+    a1.setDi2(b2);
+    a2.setDi2(b1);
+
+    assertSame(a1.getDi2(), b2);
+    assertSame(a2.getDi2(), b1);
+
+    a2.setDi2(b2);
+
+    assertSame(a1.getDi2(), b2);
+    assertSame(a2.getDi2(), b2);
+
+    a2.clearDi2();
+
+    assertSame(a1.getDi2(), b2);
+    assertNull(a2.getDi2());
+
+    assertTrue(a1.hasDi2());
+    assertFalse(a2.hasDi2());
+    assertFalse(a3.hasDi2());
+  }
+
+
+  /**
+   * rel A.Di3* -> B;
+   */
+  @Test
+  void testDi3() {
+    setup();
+    a1.addDi3(b1);
+    a1.addDi3(b2);
+    a1.addDi3(b3);
+    a2.addDi3(b2);
+
+    assertEquals(a1.getDi3s(), Arrays.asList(b1, b2, b3));
+    assertEquals(a1.getDi3List(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.getDi3s(), Arrays.asList(b2));
+    assertEquals(a2.getDi3List(), Arrays.asList(b2));
+    assertEquals(a3.getDi3s(), Arrays.asList());
+    assertEquals(a3.getDi3List(), Arrays.asList());
+
+    a1.addDi3(b1);
+    a2.addDi3(b1);
+    a2.addDi3(b2);
+
+    assertEquals(a1.getDi3s(), Arrays.asList(b1, b2, b3, b1));
+    assertEquals(a1.getDi3List(), Arrays.asList(b1, b2, b3, b1));
+    assertEquals(a2.getDi3s(), Arrays.asList(b2, b1, b2));
+    assertEquals(a2.getDi3List(), Arrays.asList(b2, b1, b2));
+    assertEquals(a3.getDi3s(), Arrays.asList());
+    assertEquals(a3.getDi3List(), Arrays.asList());
+
+    a1.removeDi3(b1);
+    a2.removeDi3(b2);
+
+    assertEquals(a1.getDi3s(), Arrays.asList(b2, b3, b1));
+    assertEquals(a1.getDi3List(), Arrays.asList(b2, b3, b1));
+    assertEquals(a2.getDi3s(), Arrays.asList(b1, b2));
+    assertEquals(a2.getDi3List(), Arrays.asList(b1, b2));
+    assertEquals(a3.getDi3s(), Arrays.asList());
+    assertEquals(a3.getDi3List(), Arrays.asList());
+  }
+
+
+  /**
+   * rel A.Bi1 <-> B.Bi1;
+   */
+
+
+  @Test
+  void testBi11() {
+    // Init
+    setup();
+    a1.setBi1(b1);
+    a2.setBi1(b2);
+
+    // Change
+    a2.setBi1(b1);
+
+    assertNull(a1.getBi1());
+    assertSame(a2.getBi1(), b1);
+    assertSame(b1.getBi1(), a2);
+    assertNull(b2.getBi1());
+  }
+
+  @Test
+  void testBi12() {
+    // Init
+    setup();
+    a1.setBi1(b2);
+
+    // Change
+    a2.setBi1(b2);
+
+    assertNull(a1.getBi1());
+    assertSame(a2.getBi1(), b2);
+    assertNull(b1.getBi1());
+    assertSame(b2.getBi1(), a2);
+  }
+
+
+  /**
+   * rel A.Bi2 <-> B.Bi2?;
+   */
+
+  @Test
+  void testBi21() {
+    // Init
+    setup();
+    a1.setBi2(b1);
+    a2.setBi2(b2);
+
+    // Change
+    a2.setBi2(b1);
+
+    assertNull(a1.getBi2());
+    assertSame(a2.getBi2(), b1);
+    assertSame(b1.getBi2(), a2);
+    assertNull(b2.getBi2());
+  }
+
+  @Test
+  void testBi22() {
+    // Init
+    setup();
+    a1.setBi2(b2);
+
+    // Change
+    a2.setBi2(b2);
+
+    assertNull(a1.getBi2());
+    assertSame(a2.getBi2(), b2);
+    assertNull(b1.getBi2());
+    assertSame(b2.getBi2(), a2);
+  }
+
+
+  /**
+   * rel A.Bi3 <-> B.Bi3*;
+   */
+  @Test
+  void testBi3() {
+    setup();
+    a2.setBi3(b2);
+
+    assertNull(a1.getBi3());
+    assertSame(a2.getBi3(), b2);
+    assertEquals(b1.getBi3s(), Arrays.asList());
+    assertEquals(b1.getBi3List(), Arrays.asList());
+    assertEquals(b2.getBi3s(), Arrays.asList(a2));
+    assertEquals(b2.getBi3List(), Arrays.asList(a2));
+    assertEquals(b3.getBi3s(), Arrays.asList());
+    assertEquals(b3.getBi3List(), Arrays.asList());
+
+    a2.setBi3(b3);
+
+    assertNull(a1.getBi3());
+    assertSame(a2.getBi3(), b3);
+    assertEquals(b1.getBi3s(), Arrays.asList());
+    assertEquals(b1.getBi3List(), Arrays.asList());
+    assertEquals(b2.getBi3s(), Arrays.asList());
+    assertEquals(b2.getBi3List(), Arrays.asList());
+    assertEquals(b3.getBi3s(), Arrays.asList(a2));
+    assertEquals(b3.getBi3List(), Arrays.asList(a2));
+
+    a1.setBi3(b3);
+    a3.setBi3(b3);
+
+    assertSame(a1.getBi3(), b3);
+    assertSame(a2.getBi3(), b3);
+    assertSame(a3.getBi3(), b3);
+    assertEquals(b1.getBi3s(), Arrays.asList());
+    assertEquals(b1.getBi3List(), Arrays.asList());
+    assertEquals(b2.getBi3s(), Arrays.asList());
+    assertEquals(b2.getBi3List(), Arrays.asList());
+    assertEquals(b3.getBi3s(), Arrays.asList(a2, a1, a3));
+    assertEquals(b3.getBi3List(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi3(b1);
+
+    assertSame(a1.getBi3(), b3);
+    assertSame(a2.getBi3(), b1);
+    assertSame(a3.getBi3(), b3);
+    assertEquals(b1.getBi3s(), Arrays.asList(a2));
+    assertEquals(b1.getBi3List(), Arrays.asList(a2));
+    assertEquals(b2.getBi3s(), Arrays.asList());
+    assertEquals(b2.getBi3List(), Arrays.asList());
+    assertEquals(b3.getBi3s(), Arrays.asList(a1, a3));
+    assertEquals(b3.getBi3List(), Arrays.asList(a1, a3));
+
+    try {
+      a2.setBi3(null);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+
+  /**
+   * rel A.Bi4? <-> B.Bi4;
+   */
+  @Test
+  void testBi41() {
+    // Init
+    setup();
+    a1.setBi4(b1);
+    a2.setBi4(b2);
+
+    // Change
+    a2.setBi4(b1);
+
+    assertNull(a1.getBi4());
+    assertSame(a2.getBi4(), b1);
+    assertSame(b1.getBi4(), a2);
+    assertNull(b2.getBi4());
+  }
+
+  @Test
+  void testBi42() {
+    // Init
+    setup();
+    a1.setBi4(b2);
+
+    // Change
+    a2.setBi4(b2);
+
+    assertNull(a1.getBi4());
+    assertSame(a2.getBi4(), b2);
+    assertNull(b1.getBi4());
+    assertSame(b2.getBi4(), a2);
+  }
+
+
+  /**
+   * rel A.Bi5? <-> B.Bi5?;
+   */
+  @Test
+  void testBi51() {
+    // Init
+    setup();
+    a1.setBi5(b1);
+    a2.setBi5(b2);
+
+    // Change
+    a2.setBi5(b1);
+
+    assertNull(a1.getBi5());
+    assertSame(a2.getBi5(), b1);
+    assertSame(b1.getBi5(), a2);
+    assertNull(b2.getBi5());
+  }
+
+  @Test
+  void testBi52() {
+    // Init
+    setup();
+    a1.setBi5(b2);
+
+    // Change
+    a2.setBi5(b2);
+
+    assertNull(a1.getBi5());
+    assertSame(a2.getBi5(), b2);
+    assertNull(b1.getBi5());
+    assertSame(b2.getBi5(), a2);
+  }
+
+
+  /**
+   * rel A.Bi6? <-> B.Bi6*;
+   */
+  @Test
+  void testBi6() {
+    setup();
+    a2.setBi6(b2);
+
+    assertNull(a1.getBi6());
+    assertSame(a2.getBi6(), b2);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList(a2));
+    assertEquals(b2.getBi6List(), Arrays.asList(a2));
+    assertEquals(b3.getBi6s(), Arrays.asList());
+    assertEquals(b3.getBi6List(), Arrays.asList());
+
+    a2.setBi6(b3);
+
+    assertNull(a1.getBi6());
+    assertSame(a2.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a2));
+    assertEquals(b3.getBi6List(), Arrays.asList(a2));
+
+    a1.setBi6(b3);
+    a3.setBi6(b3);
+
+    assertSame(a1.getBi6(), b3);
+    assertSame(a2.getBi6(), b3);
+    assertSame(a3.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a2, a1, a3));
+    assertEquals(b3.getBi6List(), Arrays.asList(a2, a1, a3));
+
+    a2.setBi6(b1);
+
+    assertSame(a1.getBi6(), b3);
+    assertSame(a2.getBi6(), b1);
+    assertSame(a3.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList(a2));
+    assertEquals(b1.getBi6List(), Arrays.asList(a2));
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a1, a3));
+    assertEquals(b3.getBi6List(), Arrays.asList(a1, a3));
+
+    a2.clearBi6();
+
+    assertSame(a1.getBi6(), b3);
+    assertNull(a2.getBi6());
+    assertSame(a3.getBi6(), b3);
+    assertEquals(b1.getBi6s(), Arrays.asList());
+    assertEquals(b1.getBi6List(), Arrays.asList());
+    assertEquals(b2.getBi6s(), Arrays.asList());
+    assertEquals(b2.getBi6List(), Arrays.asList());
+    assertEquals(b3.getBi6s(), Arrays.asList(a1, a3));
+    assertEquals(b3.getBi6List(), Arrays.asList(a1, a3));
+
+    assertTrue(a1.hasBi6());
+    assertFalse(a2.hasBi6());
+    assertTrue(a3.hasBi6());
+  }
+
+
+  /**
+   * rel A.Bi7* <-> B.Bi7;
+   */
+  @Test
+  void testBi7() {
+    setup();
+    a2.addBi7(b2);
+
+    assertEquals(a1.getBi7s(), Arrays.asList());
+    assertEquals(a1.getBi7List(), Arrays.asList());
+    assertEquals(a2.getBi7s(), Arrays.asList(b2));
+    assertEquals(a2.getBi7List(), Arrays.asList(b2));
+    assertNull(b1.getBi7());
+    assertSame(b2.getBi7(), a2);
+    assertNull(b3.getBi7());
+
+    a2.addBi7(b3);
+    a1.addBi7(b2);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertNull(b1.getBi7());
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+
+    a1.addBi7(b1);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertSame(b1.getBi7(), a1);
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+
+    a1.addBi7(b1);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertSame(b1.getBi7(), a1);
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+
+    a1.removeBi7(b1);
+
+    assertEquals(a1.getBi7s(), Arrays.asList(b2));
+    assertEquals(a1.getBi7List(), Arrays.asList(b2));
+    assertEquals(a2.getBi7s(), Arrays.asList(b3));
+    assertEquals(a2.getBi7List(), Arrays.asList(b3));
+    assertNull(b1.getBi7());
+    assertSame(b2.getBi7(), a1);
+    assertSame(b3.getBi7(), a2);
+  }
+
+
+  /**
+   * rel A.Bi8* <-> B.Bi8?;
+   */
+  @Test
+  void testBi8() {
+    setup();
+    a2.addBi8(b2);
+
+    assertEquals(a1.getBi8s(), Arrays.asList());
+    assertEquals(a1.getBi8List(), Arrays.asList());
+    assertEquals(a2.getBi8s(), Arrays.asList(b2));
+    assertEquals(a2.getBi8List(), Arrays.asList(b2));
+    assertNull(b1.getBi8());
+    assertSame(b2.getBi8(), a2);
+    assertNull(b3.getBi8());
+
+    a2.addBi8(b3);
+    a1.addBi8(b2);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertNull(b1.getBi8());
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+
+    a1.addBi8(b1);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertSame(b1.getBi8(), a1);
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+
+    a1.addBi8(b1);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2, b1));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2, b1));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertSame(b1.getBi8(), a1);
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+
+    a1.removeBi8(b1);
+
+    assertEquals(a1.getBi8s(), Arrays.asList(b2));
+    assertEquals(a1.getBi8List(), Arrays.asList(b2));
+    assertEquals(a2.getBi8s(), Arrays.asList(b3));
+    assertEquals(a2.getBi8List(), Arrays.asList(b3));
+    assertNull(b1.getBi8());
+    assertSame(b2.getBi8(), a1);
+    assertSame(b3.getBi8(), a2);
+  }
+
+
+  /**
+   * rel A.Bi9* <-> B.Bi9*;
+   */
+  @Test
+  void testBi9() {
+    setup();
+    a1.addBi9(b1);
+    a1.addBi9(b2);
+
+    assertEquals(a1.getBi9s(), Arrays.asList(b1, b2));
+    assertEquals(a1.getBi9List(), Arrays.asList(b1, b2));
+    assertEquals(a2.getBi9s(), Arrays.asList());
+    assertEquals(a2.getBi9List(), Arrays.asList());
+    assertEquals(a3.getBi9s(), Arrays.asList());
+    assertEquals(a3.getBi9List(), Arrays.asList());
+    assertEquals(b1.getBi9s(), Arrays.asList(a1));
+    assertEquals(b1.getBi9List(), Arrays.asList(a1));
+    assertEquals(b2.getBi9s(), Arrays.asList(a1));
+    assertEquals(b2.getBi9List(), Arrays.asList(a1));
+    assertEquals(b3.getBi9s(), Arrays.asList());
+    assertEquals(b3.getBi9List(), Arrays.asList());
+
+    b3.addBi9(a1);
+    b3.addBi9(a3);
+    b3.addBi9(a1);
+
+    assertEquals(a1.getBi9s(), Arrays.asList(b1, b2, b3, b3));
+    assertEquals(a1.getBi9List(), Arrays.asList(b1, b2, b3, b3));
+    assertEquals(a2.getBi9s(), Arrays.asList());
+    assertEquals(a2.getBi9List(), Arrays.asList());
+    assertEquals(a3.getBi9s(), Arrays.asList(b3));
+    assertEquals(a3.getBi9List(), Arrays.asList(b3));
+    assertEquals(b1.getBi9s(), Arrays.asList(a1));
+    assertEquals(b1.getBi9List(), Arrays.asList(a1));
+    assertEquals(b2.getBi9s(), Arrays.asList(a1));
+    assertEquals(b2.getBi9List(), Arrays.asList(a1));
+    assertEquals(b3.getBi9s(), Arrays.asList(a1, a3, a1));
+    assertEquals(b3.getBi9List(), Arrays.asList(a1, a3, a1));
+
+    b3.removeBi9(a1);
+
+    assertEquals(a1.getBi9s(), Arrays.asList(b1, b2, b3));
+    assertEquals(a1.getBi9List(), Arrays.asList(b1, b2, b3));
+    assertEquals(a2.getBi9s(), Arrays.asList());
+    assertEquals(a2.getBi9List(), Arrays.asList());
+    assertEquals(a3.getBi9s(), Arrays.asList(b3));
+    assertEquals(a3.getBi9List(), Arrays.asList(b3));
+    assertEquals(b1.getBi9s(), Arrays.asList(a1));
+    assertEquals(b1.getBi9List(), Arrays.asList(a1));
+    assertEquals(b2.getBi9s(), Arrays.asList(a1));
+    assertEquals(b2.getBi9List(), Arrays.asList(a1));
+    assertEquals(b3.getBi9s(), Arrays.asList(a3, a1));
+    assertEquals(b3.getBi9List(), Arrays.asList(a3, a1));
+  }
+
+
+  @Test
+  void testImmutableList() {
+    setup();
+
+    a1.addDi3(b1);
+    a1.addDi3(b2);
+    try {
+      a1.getDi3s().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addBi7(b1);
+    a1.addBi7(b2);
+    try {
+      a1.getBi7s().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+
+    a1.addBi9(b1);
+    a1.addBi9(b2);
+    try {
+      a1.getBi9s().add(b3);
+      fail("should throw an exception");
+    } catch (Exception e) {
+      // OK
+    }
+  }
+
+  @BeforeEach
+  void setup() {
+    r = new Root();
+    a1 = new A("a1");
+    a2 = new A("a2");
+    a3 = new A("a3");
+    b1 = new B("b1");
+    b2 = new B("b2");
+    b3 = new B("b3");
+
+    r.addA(a1);
+    r.addA(a2);
+    r.addA(a3);
+    r.addB(b1);
+    r.addB(b2);
+    r.addB(b3);
+  }
+}
diff --git a/src/test/java/org/jastadd/relast/tests/TestHelpers.java b/src/test/java/org/jastadd/relast/tests/TestHelpers.java
new file mode 100644
index 0000000..47953c6
--- /dev/null
+++ b/src/test/java/org/jastadd/relast/tests/TestHelpers.java
@@ -0,0 +1,40 @@
+package org.jastadd.relast.tests;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+class TestHelpers {
+
+  static int exec(Class klass, String[] args, File err) throws IOException,
+      InterruptedException {
+    String javaHome = System.getProperty("java.home");
+    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
+    String classpath = System.getProperty("java.class.path");
+    String className = klass.getName();
+
+    String[] newArgs = new String[args.length + 4];
+    newArgs[0] = javaBin;
+    newArgs[1] = "-cp";
+    newArgs[2] = classpath;
+    newArgs[3] = className;
+    System.arraycopy(args, 0, newArgs, 4, args.length);
+
+    ProcessBuilder builder = new ProcessBuilder(newArgs);
+    builder.redirectError(err);
+
+    Process process = builder.start();
+    process.waitFor();
+    return process.exitValue();
+  }
+
+
+  static String readFile(String path, Charset encoding)
+      throws IOException {
+    byte[] encoded = Files.readAllBytes(Paths.get(path));
+    return new String(encoded, encoding);
+  }
+
+}
diff --git a/src/test/java/org/jastadd/relast/tests/testsuite/TestSuite.java b/src/test/java/org/jastadd/relast/tests/testsuite/TestSuite.java
deleted file mode 100644
index 3515853..0000000
--- a/src/test/java/org/jastadd/relast/tests/testsuite/TestSuite.java
+++ /dev/null
@@ -1,226 +0,0 @@
-package org.jastadd.relast.tests.testsuite;
-
-//import static org.junit.Assert.*;
-//
-//import java.io.BufferedReader;
-//import java.io.BufferedWriter;
-//import java.io.ByteArrayOutputStream;
-//import java.io.File;
-//import java.io.FileNotFoundException;
-//import java.io.FileOutputStream;
-//import java.io.FileReader;
-//import java.io.IOException;
-//import java.io.InputStream;
-//import java.io.InputStreamReader;
-//import java.io.OutputStreamWriter;
-//import java.io.PrintStream;
-//import java.io.Reader;
-//import java.io.StringReader;
-//import java.io.Writer;
-//import java.util.concurrent.TimeUnit;
-//
-//import org.jastadd.relast.ast.*;
-
-abstract public class TestSuite {
-//	protected static final String TEST_FILES_PATH = "testfiles/";
-//	protected static final int DEFAULT_EXECUTION_TIMEOUT = 3000; // In ms
-//
-//	/**
-//	 * Parse a program and return the corresponding AST.
-//	 * The test will fail if the program contains semantic errors.
-//	 */
-//	protected static Program parseValidProgram(String program) {
-//		Program p = parseProgram(program);
-//		assertEquals("Program contains errors", "", getErrors(p));
-//		return p;
-//	}
-//
-//	/**
-//	 * Parse a program given a filename and return the corresponding AST.
-//	 * The test will fail if the program contains semantic errors.
-//	 */
-//	protected static Program parseValidProgramFile(String filename) {
-//		return parseValidProgramFile(new File(TEST_FILES_PATH, filename));
-//	}
-//
-//	/**
-//	 * Parse a program given a file object and return the corresponding AST.
-//	 * The test will fail if the program contains semantic errors.
-//	 */
-//	protected static Program parseValidProgramFile(File file) {
-//		Program p = parseProgramFile(file);
-//		assertEquals("Program in file " + file.getAbsolutePath() + " contains errors", "", getErrors(p));
-//		return p;
-//	}
-//
-//
-//	/**
-//	 * Parse a program given a filename and compare the errors
-//	 * with expected errors that are read from an error file.
-//	 */
-//	protected static void checkErrors(String filename) {
-//		Program p = parseProgramFile(filename + ".dia");
-//		String expectedErrors = readTestFile(filename + ".err");
-//		String actualErrors = getErrors(p);
-//		assertEquals(expectedErrors.trim(), actualErrors.trim());
-//	}
-//
-//	protected static void checkSyntaxErrors(String filename) {
-//		String actualSyntaxError = "";
-//		try {
-//			parseProgramFile(filename + ".dia");
-//		} catch (StateScanner.ScannerError e) {
-//			actualSyntaxError = e.getMessage();
-//		}
-//		String expectedSyntaxError = readTestFile(filename + ".err");
-//		assertEquals(expectedSyntaxError.trim(), actualSyntaxError.trim());
-//	}
-//
-//	/**
-//	 * Parse a program given a filename and return the corresponding AST.
-//	 */
-//	protected static Program parseProgramFile(String filename) {
-//		return parseProgramFile(new File(TEST_FILES_PATH, filename));
-//	}
-//
-//	/**
-//	 * Parse a program given a filename and return the corresponding AST.
-//	 */
-//	protected static Program parseProgramFile(File file) {
-//		try {
-//			Reader r = new FileReader(file);
-//			return parseProgram(r, "Error when parsing the file " + file.getAbsolutePath());
-//		} catch (FileNotFoundException e) {
-//			fail(e.getMessage());
-//			return null;
-//		}
-//	}
-//
-//
-//	/**
-//	 * Parse a program and return the corresponding AST.
-//	 */
-//	protected static Program parseProgram(String program) {
-//		Reader r = new StringReader(program);
-//		return parseProgram(r, "Error when parsing string " + program);
-//	}
-//
-//	/**
-//	 * Parse a program and return the corresponding AST.
-//	 */
-//	protected static Program parseProgram(Reader reader, String errorMessage) {
-//		PrintStream err = System.err;
-//		ByteArrayOutputStream os = new ByteArrayOutputStream();
-//		PrintStream ps = new PrintStream(os);
-//
-//		try {
-//			System.setErr(ps);
-//
-//			CompilationUnit cu = parse(reader);
-//			if (os.size() > 0) {
-//				// The parser should not recover from anything, such errors
-//				// should not be put in a test case (otherwise it should be
-//				// stated explicitly).
-//				fail("Parser recovery:\n" + os.toString());
-//			}
-//			Program p = new Program();
-//			p.addCompilationUnit(cu);
-//			return p;
-//		} catch (Exception e) {
-//			fail(errorMessage + ":\n " + e.getMessage() + "\n" + os.toString());
-//			return null; // This line is required to remove compile errors...
-//		} finally {
-//			System.setErr(err);
-//		}
-//	}
-//
-//	protected static String getErrors(Program p) {
-//		StringBuilder sb = new StringBuilder();
-//		for (CompilationUnit cu: p.getCompilationUnits()) {
-//			/*for (ErrorMessage e: cu.errors()) {
-//				sb.append(e + "\n");
-//			}*/
-//		}
-//		return sb.toString();
-//	}
-//
-//	//-----------------------------------------------------------
-//	//
-//	// Helper methods
-//	//
-//	//-----------------------------------------------------------
-//
-//	protected static String readTestFile(String filename) {
-//		return readFile(TEST_FILES_PATH + filename);
-//	}
-//	protected static String readFile(String filename) {
-//		StringBuilder sb = new StringBuilder();
-//		BufferedReader br = null;
-//		try {
-//			File file = new File(filename);
-//			br = new BufferedReader(new FileReader(file));
-//			String line;
-//			while ((line = br.readLine()) != null) {
-//			  sb.append(line).append("\n");
-//			}
-//		} catch (IOException e) {
-//			fail(e.getMessage());
-//		} finally {
-//			if (br != null) {
-//				{ try { br.close();} catch (IOException ignored) { } }
-//			}
-//		}
-//		return sb.toString();
-//	}
-//
-//	protected static void writeToFile(String filename, String content) {
-//		try (Writer writer = new BufferedWriter(new OutputStreamWriter(
-//				new FileOutputStream(filename), "utf-8"))) {
-//			writer.write(content);
-//		} catch (Exception e) {
-//			fail(e.getMessage());
-//		}
-//	}
-//
-//	protected static String execute(java.util.List<String> cmd) throws IOException, InterruptedException {
-//		return execute(cmd, DEFAULT_EXECUTION_TIMEOUT);
-//	}
-//	protected static String execute(java.util.List<String> cmd, int timeoutMillis) throws IOException, InterruptedException {
-//		ProcessBuilder pb = new ProcessBuilder(cmd);
-//		Process process = pb.start();
-//		process.getOutputStream().close();
-//
-//		if (!process.waitFor(timeoutMillis, TimeUnit.MILLISECONDS)) {
-//			process.destroy();
-//			fail("Command " + cmd + " could not complete within " + timeoutMillis + " ms");
-//		}
-//
-//		String standardError = inputStreamToString(process.getErrorStream());
-//		assertEquals("Standard error was not empty", "", standardError);
-//		assertEquals("Exit code was not zero => error occured", 0, process.exitValue());
-//
-//		return inputStreamToString(process.getInputStream());
-//	}
-//
-//	protected static String inputStreamToString(InputStream is) {
-//		StringBuilder sb = new StringBuilder();
-//		try {
-//			BufferedReader br = new BufferedReader(new InputStreamReader(is));
-//			String line;
-//			while ((line = br.readLine()) != null) {
-//				sb.append(line).append("\n");
-//			}
-//		} catch (IOException e) {
-//			throw new RuntimeException(e.getMessage());
-//		}
-//		return sb.toString();
-//	}
-//
-//	private static CompilationUnit parse(Reader reader) throws IOException,
-//			beaver.Parser.Exception {
-//		StateScanner scanner = new StateScanner(reader);
-//		StateParser parser = new StateParser();
-//		return (CompilationUnit) parser.parse(scanner);
-//	}
-//
-}
diff --git a/tests/multipleFiles/.gitignore b/tests/multipleFiles/.gitignore
index 77ed314..bcf9cd3 100644
--- a/tests/multipleFiles/.gitignore
+++ b/tests/multipleFiles/.gitignore
@@ -1,5 +1,5 @@
 AST/*
-All.ast
-All.jadd
+Relations.ast
+Relations.jadd
 AllGen.jadd
 AllGen.ast
\ No newline at end of file
diff --git a/tests/multipleFiles/All.ast b/tests/multipleFiles/All.ast
new file mode 100644
index 0000000..6241574
--- /dev/null
+++ b/tests/multipleFiles/All.ast
@@ -0,0 +1,3 @@
+B ::= <Name:String> <_impl_Bi9:ArrayList<A>> <_impl_Bi4:A> <_impl_Bi5:A> <_impl_Bi1:A> <_impl_Bi8:A> <_impl_Bi7:A> <_impl_Bi2:A> <_impl_Bi3:ArrayList<A>> <_impl_Bi6:ArrayList<A>>;
+Root ::= A* B*;
+A ::= <Name:String> <_impl_Bi1:B> <_impl_Bi7:ArrayList<B>> <_impl_Bi5:B> <_impl_Di1:B> <_impl_Bi3:B> <_impl_Di3:ArrayList<B>> <_impl_Bi8:ArrayList<B>> <_impl_Di2:B> <_impl_Bi2:B> <_impl_Bi9:ArrayList<B>> <_impl_Bi6:B> <_impl_Bi4:B>;
diff --git a/tests/multipleFiles/All.jadd b/tests/multipleFiles/All.jadd
new file mode 100644
index 0000000..f6e7bca
--- /dev/null
+++ b/tests/multipleFiles/All.jadd
@@ -0,0 +1,560 @@
+import java.util.ArrayList;
+import java.util.Collections;
+aspect RelAstAPI {
+  public B.B(String Name) {
+    setName(Name);
+  }
+  public A.A(String Name) {
+    setName(Name);
+  }
+  // rel A.Di1 -> B
+  public B A.getDi1() {
+    return get_impl_Di1();
+  }
+  public void A.setDi1(B o) {
+    assertNotNull(o);
+    set_impl_Di1(o);
+  }
+
+  // rel A.Di2? -> B
+  public B A.getDi2() {
+    return get_impl_Di2();
+  }
+  public void A.setDi2(B o) {
+    set_impl_Di2(o);
+  }
+  public boolean A.hasDi2() {
+    return getDi2() != null;
+  }
+  public void A.clearDi2() {
+    setDi2(null);
+  }
+
+  // rel A.Di3* -> B
+  public java.util.List<B> A.getDi3s() {
+    return getDi3List();
+  }
+  public java.util.List<B> A.getDi3List() {
+    ArrayList<B> l = get_impl_Di3();
+    return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();
+  }
+  public void A.addDi3(B o) {
+    assertNotNull(o);
+    ArrayList<B> list = get_impl_Di3();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    list.add(o);
+    set_impl_Di3(list);
+  }
+  public void A.removeDi3(B o) {
+    assertNotNull(o);
+    ArrayList<B> list = get_impl_Di3();
+    if (list != null && list.remove(o)) {
+      set_impl_Di3(list);
+    }
+  }
+
+  // rel A.Bi1 <-> B.Bi1
+  public B A.getBi1() {
+    return get_impl_Bi1();
+  }
+  public void A.setBi1(B o) {
+    assertNotNull(o);
+    if (get_impl_Bi1() != null) {
+      get_impl_Bi1().set_impl_Bi1(null);
+    }
+    if (o != null && o.get_impl_Bi1() != null) {
+      o.get_impl_Bi1().set_impl_Bi1(null);
+    }
+    set_impl_Bi1(o);
+    o.set_impl_Bi1(this);
+  }
+  public A B.getBi1() {
+    return get_impl_Bi1();
+  }
+  public void B.setBi1(A o) {
+    assertNotNull(o);
+    if (get_impl_Bi1() != null) {
+      get_impl_Bi1().set_impl_Bi1(null);
+    }
+    if (o != null && o.get_impl_Bi1() != null) {
+      o.get_impl_Bi1().set_impl_Bi1(null);
+    }
+    set_impl_Bi1(o);
+    o.set_impl_Bi1(this);
+  }
+
+  // rel A.Bi2 <-> B.Bi2?
+  public B A.getBi2() {
+    return get_impl_Bi2();
+  }
+  public void A.setBi2(B o) {
+    assertNotNull(o);
+    if (get_impl_Bi2() != null) {
+      get_impl_Bi2().set_impl_Bi2(null);
+    }
+    if (o != null && o.get_impl_Bi2() != null) {
+      o.get_impl_Bi2().set_impl_Bi2(null);
+    }
+    set_impl_Bi2(o);
+    o.set_impl_Bi2(this);
+  }
+  public A B.getBi2() {
+    return get_impl_Bi2();
+  }
+  public void B.setBi2(A o) {
+    if (get_impl_Bi2() != null) {
+      get_impl_Bi2().set_impl_Bi2(null);
+    }
+    if (o != null && o.get_impl_Bi2() != null) {
+      o.get_impl_Bi2().set_impl_Bi2(null);
+    }
+    set_impl_Bi2(o);
+    if (o != null) {
+      o.set_impl_Bi2(this);
+    }
+  }
+  public boolean B.hasBi2() {
+    return getBi2() != null;
+  }
+  public void B.clearBi2() {
+    setBi2(null);
+  }
+
+  // rel A.Bi3 <-> B.Bi3*
+  public B A.getBi3() {
+    return get_impl_Bi3();
+  }
+  public void A.setBi3(B o) {
+    assertNotNull(o);
+    if (get_impl_Bi3() != null) {
+      ArrayList<A> list2 = get_impl_Bi3().get_impl_Bi3();
+      list2.remove(this);
+      get_impl_Bi3().set_impl_Bi3(list2);
+    }
+    set_impl_Bi3(o);
+    ArrayList<A> list = o.get_impl_Bi3();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    list.add(this);
+    o.set_impl_Bi3(list);
+  }
+  public java.util.List<A> B.getBi3s() {
+    return getBi3List();
+  }
+  public java.util.List<A> B.getBi3List() {
+    ArrayList<A> l = get_impl_Bi3();
+    return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();
+  }
+  public void B.addBi3(A o) {
+    assertNotNull(o);
+    if (o != null && o.get_impl_Bi3() != null) {
+      ArrayList<A> list2 = o.get_impl_Bi3().get_impl_Bi3();
+      if (list2.remove(o))
+        o.get_impl_Bi3().set_impl_Bi3(list2);
+    }
+    ArrayList<A> list = get_impl_Bi3();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    list.add(o);
+    set_impl_Bi3(list);
+    o.set_impl_Bi3(this);
+  }
+  public void B.removeBi3(A o) {
+    assertNotNull(o);
+    ArrayList<A> list = get_impl_Bi3();
+    if (list != null && list.remove(o)) {
+      set_impl_Bi3(list);
+      if (o.get_impl_Bi3() == this) {
+        o.set_impl_Bi3(null);
+      }
+    }
+  }
+
+  // rel A.Bi4? <-> B.Bi4
+  public B A.getBi4() {
+    return get_impl_Bi4();
+  }
+  public void A.setBi4(B o) {
+    if (get_impl_Bi4() != null) {
+      get_impl_Bi4().set_impl_Bi4(null);
+    }
+    if (o != null && o.get_impl_Bi4() != null) {
+      o.get_impl_Bi4().set_impl_Bi4(null);
+    }
+    set_impl_Bi4(o);
+    if (o != null) {
+      o.set_impl_Bi4(this);
+    }
+  }
+  public boolean A.hasBi4() {
+    return getBi4() != null;
+  }
+  public void A.clearBi4() {
+    setBi4(null);
+  }
+  public A B.getBi4() {
+    return get_impl_Bi4();
+  }
+  public void B.setBi4(A o) {
+    assertNotNull(o);
+    if (get_impl_Bi4() != null) {
+      get_impl_Bi4().set_impl_Bi4(null);
+    }
+    if (o != null && o.get_impl_Bi4() != null) {
+      o.get_impl_Bi4().set_impl_Bi4(null);
+    }
+    set_impl_Bi4(o);
+    o.set_impl_Bi4(this);
+  }
+
+  // rel A.Bi5? <-> B.Bi5?
+  public B A.getBi5() {
+    return get_impl_Bi5();
+  }
+  public void A.setBi5(B o) {
+    if (get_impl_Bi5() != null) {
+      get_impl_Bi5().set_impl_Bi5(null);
+    }
+    if (o != null && o.get_impl_Bi5() != null) {
+      o.get_impl_Bi5().set_impl_Bi5(null);
+    }
+    set_impl_Bi5(o);
+    if (o != null) {
+      o.set_impl_Bi5(this);
+    }
+  }
+  public boolean A.hasBi5() {
+    return getBi5() != null;
+  }
+  public void A.clearBi5() {
+    setBi5(null);
+  }
+  public A B.getBi5() {
+    return get_impl_Bi5();
+  }
+  public void B.setBi5(A o) {
+    if (get_impl_Bi5() != null) {
+      get_impl_Bi5().set_impl_Bi5(null);
+    }
+    if (o != null && o.get_impl_Bi5() != null) {
+      o.get_impl_Bi5().set_impl_Bi5(null);
+    }
+    set_impl_Bi5(o);
+    if (o != null) {
+      o.set_impl_Bi5(this);
+    }
+  }
+  public boolean B.hasBi5() {
+    return getBi5() != null;
+  }
+  public void B.clearBi5() {
+    setBi5(null);
+  }
+
+  // rel A.Bi6? <-> B.Bi6*
+  public B A.getBi6() {
+    return get_impl_Bi6();
+  }
+  public void A.setBi6(B o) {
+    if (get_impl_Bi6() != null) {
+      ArrayList<A> list2 = get_impl_Bi6().get_impl_Bi6();
+      list2.remove(this);
+      get_impl_Bi6().set_impl_Bi6(list2);
+    }
+    set_impl_Bi6(o);
+    if (o != null) {
+      ArrayList<A> list = o.get_impl_Bi6();
+      if (list == null) {
+        list = new ArrayList<>();
+      }
+      list.add(this);
+      o.set_impl_Bi6(list);
+    }
+  }
+  public boolean A.hasBi6() {
+    return getBi6() != null;
+  }
+  public void A.clearBi6() {
+    setBi6(null);
+  }
+  public java.util.List<A> B.getBi6s() {
+    return getBi6List();
+  }
+  public java.util.List<A> B.getBi6List() {
+    ArrayList<A> l = get_impl_Bi6();
+    return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();
+  }
+  public void B.addBi6(A o) {
+    assertNotNull(o);
+    if (o != null && o.get_impl_Bi6() != null) {
+      ArrayList<A> list2 = o.get_impl_Bi6().get_impl_Bi6();
+      if (list2.remove(o))
+        o.get_impl_Bi6().set_impl_Bi6(list2);
+    }
+    ArrayList<A> list = get_impl_Bi6();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    list.add(o);
+    set_impl_Bi6(list);
+    o.set_impl_Bi6(this);
+  }
+  public void B.removeBi6(A o) {
+    assertNotNull(o);
+    ArrayList<A> list = get_impl_Bi6();
+    if (list != null && list.remove(o)) {
+      set_impl_Bi6(list);
+      if (o.get_impl_Bi6() == this) {
+        o.set_impl_Bi6(null);
+      }
+    }
+  }
+
+  // rel A.Bi7* <-> B.Bi7
+  public java.util.List<B> A.getBi7s() {
+    return getBi7List();
+  }
+  public java.util.List<B> A.getBi7List() {
+    ArrayList<B> l = get_impl_Bi7();
+    return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();
+  }
+  public void A.addBi7(B o) {
+    assertNotNull(o);
+    if (o != null && o.get_impl_Bi7() != null) {
+      ArrayList<B> list2 = o.get_impl_Bi7().get_impl_Bi7();
+      if (list2.remove(o))
+        o.get_impl_Bi7().set_impl_Bi7(list2);
+    }
+    ArrayList<B> list = get_impl_Bi7();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    list.add(o);
+    set_impl_Bi7(list);
+    o.set_impl_Bi7(this);
+  }
+  public void A.removeBi7(B o) {
+    assertNotNull(o);
+    ArrayList<B> list = get_impl_Bi7();
+    if (list != null && list.remove(o)) {
+      set_impl_Bi7(list);
+      if (o.get_impl_Bi7() == this) {
+        o.set_impl_Bi7(null);
+      }
+    }
+  }
+  public A B.getBi7() {
+    return get_impl_Bi7();
+  }
+  public void B.setBi7(A o) {
+    assertNotNull(o);
+    if (get_impl_Bi7() != null) {
+      ArrayList<B> list2 = get_impl_Bi7().get_impl_Bi7();
+      list2.remove(this);
+      get_impl_Bi7().set_impl_Bi7(list2);
+    }
+    set_impl_Bi7(o);
+    ArrayList<B> list = o.get_impl_Bi7();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    list.add(this);
+    o.set_impl_Bi7(list);
+  }
+
+  // rel A.Bi8* <-> B.Bi8?
+  public java.util.List<B> A.getBi8s() {
+    return getBi8List();
+  }
+  public java.util.List<B> A.getBi8List() {
+    ArrayList<B> l = get_impl_Bi8();
+    return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();
+  }
+  public void A.addBi8(B o) {
+    assertNotNull(o);
+    if (o != null && o.get_impl_Bi8() != null) {
+      ArrayList<B> list2 = o.get_impl_Bi8().get_impl_Bi8();
+      if (list2.remove(o))
+        o.get_impl_Bi8().set_impl_Bi8(list2);
+    }
+    ArrayList<B> list = get_impl_Bi8();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    list.add(o);
+    set_impl_Bi8(list);
+    o.set_impl_Bi8(this);
+  }
+  public void A.removeBi8(B o) {
+    assertNotNull(o);
+    ArrayList<B> list = get_impl_Bi8();
+    if (list != null && list.remove(o)) {
+      set_impl_Bi8(list);
+      if (o.get_impl_Bi8() == this) {
+        o.set_impl_Bi8(null);
+      }
+    }
+  }
+  public A B.getBi8() {
+    return get_impl_Bi8();
+  }
+  public void B.setBi8(A o) {
+    if (get_impl_Bi8() != null) {
+      ArrayList<B> list2 = get_impl_Bi8().get_impl_Bi8();
+      list2.remove(this);
+      get_impl_Bi8().set_impl_Bi8(list2);
+    }
+    set_impl_Bi8(o);
+    if (o != null) {
+      ArrayList<B> list = o.get_impl_Bi8();
+      if (list == null) {
+        list = new ArrayList<>();
+      }
+      list.add(this);
+      o.set_impl_Bi8(list);
+    }
+  }
+  public boolean B.hasBi8() {
+    return getBi8() != null;
+  }
+  public void B.clearBi8() {
+    setBi8(null);
+  }
+
+  // rel A.Bi9* <-> B.Bi9*
+  public java.util.List<B> A.getBi9s() {
+    return getBi9List();
+  }
+  public java.util.List<B> A.getBi9List() {
+    ArrayList<B> l = get_impl_Bi9();
+    return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();
+  }
+  public void A.addBi9(B o) {
+    assertNotNull(o);
+    ArrayList<B> list = get_impl_Bi9();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    ArrayList<A> list2 = o.get_impl_Bi9();
+    if (list2 == null) {
+      list2 = new ArrayList<>();
+    }
+    list.add(o);
+    list2.add(this);
+    set_impl_Bi9(list);
+    o.set_impl_Bi9(list2);
+  }
+  public void A.removeBi9(B o) {
+    assertNotNull(o);
+    ArrayList<B> list = get_impl_Bi9();
+    if (list != null && list.remove(o)) {
+      ArrayList<A> list2 = o.get_impl_Bi9();
+      if (list2 != null) list2.remove(this);
+      set_impl_Bi9(list);
+      o.set_impl_Bi9(list2);
+    }
+  }
+  public java.util.List<A> B.getBi9s() {
+    return getBi9List();
+  }
+  public java.util.List<A> B.getBi9List() {
+    ArrayList<A> l = get_impl_Bi9();
+    return l != null ? Collections.unmodifiableList(l) : Collections.emptyList();
+  }
+  public void B.addBi9(A o) {
+    assertNotNull(o);
+    ArrayList<A> list = get_impl_Bi9();
+    if (list == null) {
+      list = new ArrayList<>();
+    }
+    ArrayList<B> list2 = o.get_impl_Bi9();
+    if (list2 == null) {
+      list2 = new ArrayList<>();
+    }
+    list.add(o);
+    list2.add(this);
+    set_impl_Bi9(list);
+    o.set_impl_Bi9(list2);
+  }
+  public void B.removeBi9(A o) {
+    assertNotNull(o);
+    ArrayList<A> list = get_impl_Bi9();
+    if (list != null && list.remove(o)) {
+      ArrayList<B> list2 = o.get_impl_Bi9();
+      if (list2 != null) list2.remove(this);
+      set_impl_Bi9(list);
+      o.set_impl_Bi9(list2);
+    }
+  }
+
+  public boolean ASTNode.violatesLowerBounds() {
+    return !getLowerBoundsViolations().isEmpty();
+  }
+  public java.util.List<Pair<ASTNode, String>> ASTNode.getLowerBoundsViolations() {
+    ArrayList<Pair<ASTNode, String>> list = new ArrayList<>();
+    computeLowerBoundsViolations(list);
+    return list;
+  }
+  public void ASTNode.computeLowerBoundsViolations(java.util.List<Pair<ASTNode, String>> list) {
+    for (int i = 0; i < getNumChildNoTransform(); i++) {
+      getChildNoTransform(i).computeLowerBoundsViolations(list);
+    }
+  }
+  public void B.computeLowerBoundsViolations(java.util.List<Pair<ASTNode, String>> list) {
+    if (getBi4() == null) {
+      list.add(new Pair<>(this, "Bi4"));
+    }
+    if (getBi1() == null) {
+      list.add(new Pair<>(this, "Bi1"));
+    }
+    if (getBi7() == null) {
+      list.add(new Pair<>(this, "Bi7"));
+    }
+    super.computeLowerBoundsViolations(list);
+  }
+  public void A.computeLowerBoundsViolations(java.util.List<Pair<ASTNode, String>> list) {
+    if (getBi1() == null) {
+      list.add(new Pair<>(this, "Bi1"));
+    }
+    if (getDi1() == null) {
+      list.add(new Pair<>(this, "Di1"));
+    }
+    if (getBi3() == null) {
+      list.add(new Pair<>(this, "Bi3"));
+    }
+    if (getBi2() == null) {
+      list.add(new Pair<>(this, "Bi2"));
+    }
+    super.computeLowerBoundsViolations(list);
+  }
+  public class Pair<T1, T2> {
+    public final T1 _1;
+    public final T2 _2;
+    public Pair(T1 _1, T2 _2) {
+      ASTNode.assertNotNull(_1);
+      ASTNode.assertNotNull(_2);
+      this._1 = _1;
+      this._2 = _2;
+    }
+    public boolean equals(Object other) {
+      if (other instanceof Pair) {
+        Pair<?,?> p = (Pair<?,?>) other;
+        return _1.equals(p._1) && _2.equals(p._2);
+      } else {
+        return false;
+      }
+    }
+    public int hashCode() {
+      return 31*_1.hashCode() + _2.hashCode();
+    }
+  }
+  public static void ASTNode.assertNotNull(Object obj) {
+    if (obj == null) {
+      throw new NullPointerException();
+    }
+  }
+}
diff --git a/tests/multipleFiles/Makefile b/tests/multipleFiles/Makefile
index db12f0a..9a98a4a 100644
--- a/tests/multipleFiles/Makefile
+++ b/tests/multipleFiles/Makefile
@@ -7,12 +7,12 @@ test: clean test1
 build-jar:
 	(cd ../../ && ./gradlew jar)
 test1:
-	java -jar ../../build/libs/relast.jar Rules1.relast Rules2.relast Rules3.relast --file --grammarName=All
-	java -jar ../../tools/jastadd2.jar --package=AST All.ast All.jadd Utils.jadd
-	javac AST/*.java All.java
-	java All
-	java -jar ../../build/libs/relast.jar All.ast --file --grammarName=AllGen
-	diff All.ast AllGen.ast
+	java -jar ../../build/libs/relast.jar Rules1.relast Rules2.relast Rules3.relast --file --grammarName=Relations
+	java -jar ../../tools/jastadd2.jar --package=AST Relations.ast Relations.jadd Utils.jadd
+	javac AST/*.java Relations.java
+	java Relations
+	java -jar ../../build/libs/relast.jar Relations.ast --file --grammarName=AllGen
+	diff Relations.ast AllGen.ast
 
 clean:
 	rm -rf AST
diff --git a/tests/resolverHelper/.gitignore b/tests/resolverHelper/.gitignore
index ce16505..404a3fe 100644
--- a/tests/resolverHelper/.gitignore
+++ b/tests/resolverHelper/.gitignore
@@ -1,4 +1,4 @@
 /AST/*
-/All.ast
-All.jadd
+/Relations.ast
+Relations.jadd
 AllRefResolver.jadd
diff --git a/tests/resolverHelper/All.ast b/tests/resolverHelper/All.ast
new file mode 100644
index 0000000..4e70b07
--- /dev/null
+++ b/tests/resolverHelper/All.ast
@@ -0,0 +1,9 @@
+Root ::= A* B*;
+abstract NamedElement ::= <Name:String>;
+A : NamedElement ::= <_impl_Rel1:NamedElement> <_impl_Rel2:NamedElement>;
+B : NamedElement;
+
+__unresolvedRoot : Root ::= <__token:String>;
+__unresolvedNamedElement : NamedElement ::= <__token:String>;
+__unresolvedA : A ::= <__token:String>;
+__unresolvedB : B ::= <__token:String>;
diff --git a/tests/resolverHelper/All.jadd b/tests/resolverHelper/All.jadd
new file mode 100644
index 0000000..98cade5
--- /dev/null
+++ b/tests/resolverHelper/All.jadd
@@ -0,0 +1,83 @@
+import java.util.ArrayList;
+import java.util.Collections;
+aspect RelAstAPI {
+  public A.A(String Name) {
+    setName(Name);
+  }
+  public __unresolvedA.__unresolvedA(String Name, String __token) {
+    setName(Name);
+    set__token(__token);
+  }
+  // rel A.Rel1 -> NamedElement
+  public NamedElement A.getRel1() {
+    if (get_impl_Rel1() != null && get_impl_Rel1().unresolved()) {
+      set_impl_Rel1(resolveRel1(get_impl_Rel1().asUnresolvedNamedElement().get__token()));
+    }
+    return get_impl_Rel1();
+  }
+  public void A.setRel1(NamedElement o) {
+    assertNotNull(o);
+    set_impl_Rel1(o);
+  }
+
+  // rel A.Rel2 -> NamedElement
+  public NamedElement A.getRel2() {
+    if (get_impl_Rel2() != null && get_impl_Rel2().unresolved()) {
+      set_impl_Rel2(resolveRel2(get_impl_Rel2().asUnresolvedNamedElement().get__token()));
+    }
+    return get_impl_Rel2();
+  }
+  public void A.setRel2(NamedElement o) {
+    assertNotNull(o);
+    set_impl_Rel2(o);
+  }
+
+  public boolean ASTNode.violatesLowerBounds() {
+    return !getLowerBoundsViolations().isEmpty();
+  }
+  public java.util.List<Pair<ASTNode, String>> ASTNode.getLowerBoundsViolations() {
+    ArrayList<Pair<ASTNode, String>> list = new ArrayList<>();
+    computeLowerBoundsViolations(list);
+    return list;
+  }
+  public void ASTNode.computeLowerBoundsViolations(java.util.List<Pair<ASTNode, String>> list) {
+    for (int i = 0; i < getNumChildNoTransform(); i++) {
+      getChildNoTransform(i).computeLowerBoundsViolations(list);
+    }
+  }
+  public void A.computeLowerBoundsViolations(java.util.List<Pair<ASTNode, String>> list) {
+    if (getRel1() == null) {
+      list.add(new Pair<>(this, "Rel1"));
+    }
+    if (getRel2() == null) {
+      list.add(new Pair<>(this, "Rel2"));
+    }
+    super.computeLowerBoundsViolations(list);
+  }
+  public class Pair<T1, T2> {
+    public final T1 _1;
+    public final T2 _2;
+    public Pair(T1 _1, T2 _2) {
+      ASTNode.assertNotNull(_1);
+      ASTNode.assertNotNull(_2);
+      this._1 = _1;
+      this._2 = _2;
+    }
+    public boolean equals(Object other) {
+      if (other instanceof Pair) {
+        Pair<?,?> p = (Pair<?,?>) other;
+        return _1.equals(p._1) && _2.equals(p._2);
+      } else {
+        return false;
+      }
+    }
+    public int hashCode() {
+      return 31*_1.hashCode() + _2.hashCode();
+    }
+  }
+  public static void ASTNode.assertNotNull(Object obj) {
+    if (obj == null) {
+      throw new NullPointerException();
+    }
+  }
+}
diff --git a/tests/resolverHelper/Makefile b/tests/resolverHelper/Makefile
index 3ffd58d..cc7fe4a 100644
--- a/tests/resolverHelper/Makefile
+++ b/tests/resolverHelper/Makefile
@@ -7,10 +7,10 @@ test: clean test1
 build-jar:
 	(cd ../../ && ./gradlew jar)
 test1:
-	java -jar ../../build/libs/relast.jar Grammar.relast --file --grammarName=All --resolverHelper
-	java -jar ../../tools/jastadd2.jar --package=AST --rewrite=cnta All.ast All.jadd AllRefResolver.jadd MyRefResolver.jadd Utils.jadd
-	javac AST/*.java All.java
-	java All
+	java -jar ../../build/libs/relast.jar Grammar.relast --file --grammarName=Relations --resolverHelper
+	java -jar ../../tools/jastadd2.jar --package=AST --rewrite=cnta Relations.ast Relations.jadd AllRefResolver.jadd MyRefResolver.jadd Utils.jadd
+	javac AST/*.java Relations.java
+	java Relations
 
 clean:
 	rm -rf AST
diff --git a/tests/valid/.gitignore b/tests/valid/.gitignore
deleted file mode 100644
index 670d442..0000000
--- a/tests/valid/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/AST/*
-/*Gen.ast
-/*Gen.jadd
diff --git a/tests/valid/AbstractTests.java b/tests/valid/AbstractTests.java
deleted file mode 100644
index 3b86096..0000000
--- a/tests/valid/AbstractTests.java
+++ /dev/null
@@ -1,25 +0,0 @@
-public class AbstractTests {
-	protected void assertException() {
-		check(false, "should throw exception");
-	}
-	protected void assertTrue(boolean b) {
-		check(b, "value should be true (is false)");
-	}
-	protected void assertFalse(boolean b) {
-		check(!b, "value should be flase (is true)");
-	}
-	protected void assertNull(Object obj) {
-		check(obj == null, "Object not null: " + obj);
-	}
-	protected void assertSame(Object o1, Object o2) {
-		check(o1 == o2, "Objects not same: " + o1 + ", " + o2);
-	}
-	protected void assertEquals(Object o1, Object o2) {
-		check(o1.equals(o2), "Objects not equals: " + o1 + ", " + o2);
-	}
-	protected void check(boolean b, String message) {
-		if (!b) {
-			throw new RuntimeException(message);
-		}
-	}
-}
\ No newline at end of file
diff --git a/tests/valid/All.java b/tests/valid/All.java
deleted file mode 100644
index 404f3fc..0000000
--- a/tests/valid/All.java
+++ /dev/null
@@ -1,629 +0,0 @@
-import AST.*;
-import java.util.*;
-
-@SuppressWarnings("ArraysAsListWithZeroOrOneArgument")
-public class All extends AbstractTests {
-	private Root r;
-	private A a1;
-	private A a2;
-	private A a3;
-	private B b1;
-	private B b2;
-	private B b3;
-
-	public static void main(String args[]) {
-		new All().test();
-	}
-
-	public void test() {
-		testDi1();
-		testDi2();
-		testDi3();
-
-		testBi1();
-		testBi2();
-		testBi3();
-		testBi4();
-		testBi5();
-		testBi6();
-		testBi7();
-		testBi8();
-		testBi9();
-
-		testImmutableList();
-	}
-
-	/**
-	 * rel A.Di1 -> B;
-	 */
-	private void testDi1() {
-		setup();
-		a1.setDi1(b2);
-		a2.setDi1(b1);
-
-		assertSame(a1.getDi1(), b2);
-		assertSame(a2.getDi1(), b1);
-
-		a2.setDi1(b2);
-
-		assertSame(a1.getDi1(), b2);
-		assertSame(a2.getDi1(), b2);
-
-		try {
-			a3.setDi1(null);
-			assertException();
-		} catch (Exception e) {
-			// OK
-		}
-	}
-
-
-	/**
-	 * rel A.Di2? -> B;
-	 */
-	private void testDi2() {
-		setup();
-		a1.setDi2(b2);
-		a2.setDi2(b1);
-
-		assertSame(a1.getDi2(), b2);
-		assertSame(a2.getDi2(), b1);
-
-		a2.setDi2(b2);
-
-		assertSame(a1.getDi2(), b2);
-		assertSame(a2.getDi2(), b2);
-
-		a2.clearDi2();
-
-		assertSame(a1.getDi2(), b2);
-		assertNull(a2.getDi2());
-
-		assertTrue(a1.hasDi2());
-		assertFalse(a2.hasDi2());
-		assertFalse(a3.hasDi2());
-	}
-
-
-	/**
-	 * rel A.Di3* -> B;
-	 */
-	private void testDi3() {
-		setup();
-		a1.addDi3(b1);
-		a1.addDi3(b2);
-		a1.addDi3(b3);
-		a2.addDi3(b2);
-		
-		assertEquals(a1.getDi3s(), Arrays.asList(b1, b2, b3));
-		assertEquals(a1.getDi3List(), Arrays.asList(b1, b2, b3));
-		assertEquals(a2.getDi3s(), Arrays.asList(b2));
-		assertEquals(a2.getDi3List(), Arrays.asList(b2));
-		assertEquals(a3.getDi3s(), Arrays.asList());
-		assertEquals(a3.getDi3List(), Arrays.asList());
-
-		a1.addDi3(b1);
-		a2.addDi3(b1);
-		a2.addDi3(b2);
-
-		assertEquals(a1.getDi3s(), Arrays.asList(b1, b2, b3, b1));
-		assertEquals(a1.getDi3List(), Arrays.asList(b1, b2, b3, b1));
-		assertEquals(a2.getDi3s(), Arrays.asList(b2, b1, b2));
-		assertEquals(a2.getDi3List(), Arrays.asList(b2, b1, b2));
-		assertEquals(a3.getDi3s(), Arrays.asList());
-		assertEquals(a3.getDi3List(), Arrays.asList());
-
-		a1.removeDi3(b1);
-		a2.removeDi3(b2);
-
-		assertEquals(a1.getDi3s(), Arrays.asList(b2, b3, b1));
-		assertEquals(a1.getDi3List(), Arrays.asList(b2, b3, b1));
-		assertEquals(a2.getDi3s(), Arrays.asList(b1, b2));
-		assertEquals(a2.getDi3List(), Arrays.asList(b1, b2));
-		assertEquals(a3.getDi3s(), Arrays.asList());
-		assertEquals(a3.getDi3List(), Arrays.asList());
-	}
-
-
-	/**
-	 * rel A.Bi1 <-> B.Bi1;
-	 */
-	private void testBi1() {
-		testBi11();
-		testBi12();
-	}
-	private void testBi11() {
-		// Init
-		setup();
-		a1.setBi1(b1);
-		a2.setBi1(b2);
-
-		// Change
-		a2.setBi1(b1);
-
-		assertNull(a1.getBi1());
-		assertSame(a2.getBi1(), b1);
-		assertSame(b1.getBi1(), a2);
-		assertNull(b2.getBi1());
-	}
-	private void testBi12() {
-		// Init
-		setup();
-		a1.setBi1(b2);
-
-		// Change
-		a2.setBi1(b2);
-
-		assertNull(a1.getBi1());
-		assertSame(a2.getBi1(), b2);
-		assertNull(b1.getBi1());
-		assertSame(b2.getBi1(), a2);
-	}
-
-
-
-	/**
-	 * rel A.Bi2 <-> B.Bi2?;
-	 */
-	private void testBi2() {
-		testBi21();
-		testBi22();
-	}
-	private void testBi21() {
-		// Init
-		setup();
-		a1.setBi2(b1);
-		a2.setBi2(b2);
-
-		// Change
-		a2.setBi2(b1);
-
-		assertNull(a1.getBi2());
-		assertSame(a2.getBi2(), b1);
-		assertSame(b1.getBi2(), a2);
-		assertNull(b2.getBi2());
-	}
-	private void testBi22() {
-		// Init
-		setup();
-		a1.setBi2(b2);
-
-		// Change
-		a2.setBi2(b2);
-
-		assertNull(a1.getBi2());
-		assertSame(a2.getBi2(), b2);
-		assertNull(b1.getBi2());
-		assertSame(b2.getBi2(), a2);
-	}
-
-
-
-	/**
-	 * rel A.Bi3 <-> B.Bi3*;
-	 */
-	private void testBi3() {
-		setup();
-		a2.setBi3(b2);
-
-		assertNull(a1.getBi3());
-		assertSame(a2.getBi3(), b2);
-		assertEquals(b1.getBi3s(), Arrays.asList());
-		assertEquals(b1.getBi3List(), Arrays.asList());
-		assertEquals(b2.getBi3s(), Arrays.asList(a2));
-		assertEquals(b2.getBi3List(), Arrays.asList(a2));
-		assertEquals(b3.getBi3s(), Arrays.asList());
-		assertEquals(b3.getBi3List(), Arrays.asList());
-
-		a2.setBi3(b3);
-
-		assertNull(a1.getBi3());
-		assertSame(a2.getBi3(), b3);
-		assertEquals(b1.getBi3s(), Arrays.asList());
-		assertEquals(b1.getBi3List(), Arrays.asList());
-		assertEquals(b2.getBi3s(), Arrays.asList());
-		assertEquals(b2.getBi3List(), Arrays.asList());
-		assertEquals(b3.getBi3s(), Arrays.asList(a2));
-		assertEquals(b3.getBi3List(), Arrays.asList(a2));
-
-		a1.setBi3(b3);
-		a3.setBi3(b3);
-
-		assertSame(a1.getBi3(), b3);
-		assertSame(a2.getBi3(), b3);
-		assertSame(a3.getBi3(), b3);
-		assertEquals(b1.getBi3s(), Arrays.asList());
-		assertEquals(b1.getBi3List(), Arrays.asList());
-		assertEquals(b2.getBi3s(), Arrays.asList());
-		assertEquals(b2.getBi3List(), Arrays.asList());
-		assertEquals(b3.getBi3s(), Arrays.asList(a2, a1, a3));
-		assertEquals(b3.getBi3List(), Arrays.asList(a2, a1, a3));
-
-		a2.setBi3(b1);
-
-		assertSame(a1.getBi3(), b3);
-		assertSame(a2.getBi3(), b1);
-		assertSame(a3.getBi3(), b3);
-		assertEquals(b1.getBi3s(), Arrays.asList(a2));
-		assertEquals(b1.getBi3List(), Arrays.asList(a2));
-		assertEquals(b2.getBi3s(), Arrays.asList());
-		assertEquals(b2.getBi3List(), Arrays.asList());
-		assertEquals(b3.getBi3s(), Arrays.asList(a1, a3));
-		assertEquals(b3.getBi3List(), Arrays.asList(a1, a3));
-
-		try {
-			a2.setBi3(null);
-			assertException();
-		} catch (Exception e) {
-			// OK
-		}
-	}
-
-
-
-	/**
-	 * rel A.Bi4? <-> B.Bi4;
-	 */
-	private void testBi4() {
-		testBi41();
-		testBi42();
-	}
-	private void testBi41() {
-		// Init
-		setup();
-		a1.setBi4(b1);
-		a2.setBi4(b2);
-
-		// Change
-		a2.setBi4(b1);
-
-		assertNull(a1.getBi4());
-		assertSame(a2.getBi4(), b1);
-		assertSame(b1.getBi4(), a2);
-		assertNull(b2.getBi4());
-	}
-	private void testBi42() {
-		// Init
-		setup();
-		a1.setBi4(b2);
-
-		// Change
-		a2.setBi4(b2);
-
-		assertNull(a1.getBi4());
-		assertSame(a2.getBi4(), b2);
-		assertNull(b1.getBi4());
-		assertSame(b2.getBi4(), a2);
-	}
-
-
-
-	/**
-	 * rel A.Bi5? <-> B.Bi5?;
-	 */
-	private void testBi5() {
-		testBi51();
-		testBi52();
-	}
-	private void testBi51() {
-		// Init
-		setup();
-		a1.setBi5(b1);
-		a2.setBi5(b2);
-
-		// Change
-		a2.setBi5(b1);
-
-		assertNull(a1.getBi5());
-		assertSame(a2.getBi5(), b1);
-		assertSame(b1.getBi5(), a2);
-		assertNull(b2.getBi5());
-	}
-	private void testBi52() {
-		// Init
-		setup();
-		a1.setBi5(b2);
-
-		// Change
-		a2.setBi5(b2);
-
-		assertNull(a1.getBi5());
-		assertSame(a2.getBi5(), b2);
-		assertNull(b1.getBi5());
-		assertSame(b2.getBi5(), a2);
-	}
-
-
-
-	/**
-	 * rel A.Bi6? <-> B.Bi6*;
-	 */
-	private void testBi6() {
-		setup();
-		a2.setBi6(b2);
-
-		assertNull(a1.getBi6());
-		assertSame(a2.getBi6(), b2);
-		assertEquals(b1.getBi6s(), Arrays.asList());
-		assertEquals(b1.getBi6List(), Arrays.asList());
-		assertEquals(b2.getBi6s(), Arrays.asList(a2));
-		assertEquals(b2.getBi6List(), Arrays.asList(a2));
-		assertEquals(b3.getBi6s(), Arrays.asList());
-		assertEquals(b3.getBi6List(), Arrays.asList());
-
-		a2.setBi6(b3);
-
-		assertNull(a1.getBi6());
-		assertSame(a2.getBi6(), b3);
-		assertEquals(b1.getBi6s(), Arrays.asList());
-		assertEquals(b1.getBi6List(), Arrays.asList());
-		assertEquals(b2.getBi6s(), Arrays.asList());
-		assertEquals(b2.getBi6List(), Arrays.asList());
-		assertEquals(b3.getBi6s(), Arrays.asList(a2));
-		assertEquals(b3.getBi6List(), Arrays.asList(a2));
-
-		a1.setBi6(b3);
-		a3.setBi6(b3);
-
-		assertSame(a1.getBi6(), b3);
-		assertSame(a2.getBi6(), b3);
-		assertSame(a3.getBi6(), b3);
-		assertEquals(b1.getBi6s(), Arrays.asList());
-		assertEquals(b1.getBi6List(), Arrays.asList());
-		assertEquals(b2.getBi6s(), Arrays.asList());
-		assertEquals(b2.getBi6List(), Arrays.asList());
-		assertEquals(b3.getBi6s(), Arrays.asList(a2, a1, a3));
-		assertEquals(b3.getBi6List(), Arrays.asList(a2, a1, a3));
-
-		a2.setBi6(b1);
-
-		assertSame(a1.getBi6(), b3);
-		assertSame(a2.getBi6(), b1);
-		assertSame(a3.getBi6(), b3);
-		assertEquals(b1.getBi6s(), Arrays.asList(a2));
-		assertEquals(b1.getBi6List(), Arrays.asList(a2));
-		assertEquals(b2.getBi6s(), Arrays.asList());
-		assertEquals(b2.getBi6List(), Arrays.asList());
-		assertEquals(b3.getBi6s(), Arrays.asList(a1, a3));
-		assertEquals(b3.getBi6List(), Arrays.asList(a1, a3));
-
-		a2.clearBi6();
-
-		assertSame(a1.getBi6(), b3);
-		assertNull(a2.getBi6());
-		assertSame(a3.getBi6(), b3);
-		assertEquals(b1.getBi6s(), Arrays.asList());
-		assertEquals(b1.getBi6List(), Arrays.asList());
-		assertEquals(b2.getBi6s(), Arrays.asList());
-		assertEquals(b2.getBi6List(), Arrays.asList());
-		assertEquals(b3.getBi6s(), Arrays.asList(a1, a3));
-		assertEquals(b3.getBi6List(), Arrays.asList(a1, a3));
-
-		assertTrue(a1.hasBi6());
-		assertFalse(a2.hasBi6());
-		assertTrue(a3.hasBi6());
-	}
-
-
-
-	/**
-	 * rel A.Bi7* <-> B.Bi7;
-	 */
-	private void testBi7() {
-		setup();
-		a2.addBi7(b2);
-
-		assertEquals(a1.getBi7s(), Arrays.asList());
-		assertEquals(a1.getBi7List(), Arrays.asList());
-		assertEquals(a2.getBi7s(), Arrays.asList(b2));
-		assertEquals(a2.getBi7List(), Arrays.asList(b2));
-		assertNull(b1.getBi7());
-		assertSame(b2.getBi7(), a2);
-		assertNull(b3.getBi7());
-
-		a2.addBi7(b3);
-		a1.addBi7(b2);
-
-		assertEquals(a1.getBi7s(), Arrays.asList(b2));
-		assertEquals(a1.getBi7List(), Arrays.asList(b2));
-		assertEquals(a2.getBi7s(), Arrays.asList(b3));
-		assertEquals(a2.getBi7List(), Arrays.asList(b3));
-		assertNull(b1.getBi7());
-		assertSame(b2.getBi7(), a1);
-		assertSame(b3.getBi7(), a2);
-
-		a1.addBi7(b1);
-
-		assertEquals(a1.getBi7s(), Arrays.asList(b2, b1));
-		assertEquals(a1.getBi7List(), Arrays.asList(b2, b1));
-		assertEquals(a2.getBi7s(), Arrays.asList(b3));
-		assertEquals(a2.getBi7List(), Arrays.asList(b3));
-		assertSame(b1.getBi7(), a1);
-		assertSame(b2.getBi7(), a1);
-		assertSame(b3.getBi7(), a2);
-
-		a1.addBi7(b1);
-
-		assertEquals(a1.getBi7s(), Arrays.asList(b2, b1));
-		assertEquals(a1.getBi7List(), Arrays.asList(b2, b1));
-		assertEquals(a2.getBi7s(), Arrays.asList(b3));
-		assertEquals(a2.getBi7List(), Arrays.asList(b3));
-		assertSame(b1.getBi7(), a1);
-		assertSame(b2.getBi7(), a1);
-		assertSame(b3.getBi7(), a2);
-
-		a1.removeBi7(b1);
-
-		assertEquals(a1.getBi7s(), Arrays.asList(b2));
-		assertEquals(a1.getBi7List(), Arrays.asList(b2));
-		assertEquals(a2.getBi7s(), Arrays.asList(b3));
-		assertEquals(a2.getBi7List(), Arrays.asList(b3));
-		assertNull(b1.getBi7());
-		assertSame(b2.getBi7(), a1);
-		assertSame(b3.getBi7(), a2);
-	}
-
-
-
-	/**
-	 * rel A.Bi8* <-> B.Bi8?;
-	 */
-	private void testBi8() {
-		setup();
-		a2.addBi8(b2);
-
-		assertEquals(a1.getBi8s(), Arrays.asList());
-		assertEquals(a1.getBi8List(), Arrays.asList());
-		assertEquals(a2.getBi8s(), Arrays.asList(b2));
-		assertEquals(a2.getBi8List(), Arrays.asList(b2));
-		assertNull(b1.getBi8());
-		assertSame(b2.getBi8(), a2);
-		assertNull(b3.getBi8());
-
-		a2.addBi8(b3);
-		a1.addBi8(b2);
-
-		assertEquals(a1.getBi8s(), Arrays.asList(b2));
-		assertEquals(a1.getBi8List(), Arrays.asList(b2));
-		assertEquals(a2.getBi8s(), Arrays.asList(b3));
-		assertEquals(a2.getBi8List(), Arrays.asList(b3));
-		assertNull(b1.getBi8());
-		assertSame(b2.getBi8(), a1);
-		assertSame(b3.getBi8(), a2);
-
-		a1.addBi8(b1);
-
-		assertEquals(a1.getBi8s(), Arrays.asList(b2, b1));
-		assertEquals(a1.getBi8List(), Arrays.asList(b2, b1));
-		assertEquals(a2.getBi8s(), Arrays.asList(b3));
-		assertEquals(a2.getBi8List(), Arrays.asList(b3));
-		assertSame(b1.getBi8(), a1);
-		assertSame(b2.getBi8(), a1);
-		assertSame(b3.getBi8(), a2);
-
-		a1.addBi8(b1);
-
-		assertEquals(a1.getBi8s(), Arrays.asList(b2, b1));
-		assertEquals(a1.getBi8List(), Arrays.asList(b2, b1));
-		assertEquals(a2.getBi8s(), Arrays.asList(b3));
-		assertEquals(a2.getBi8List(), Arrays.asList(b3));
-		assertSame(b1.getBi8(), a1);
-		assertSame(b2.getBi8(), a1);
-		assertSame(b3.getBi8(), a2);
-
-		a1.removeBi8(b1);
-
-		assertEquals(a1.getBi8s(), Arrays.asList(b2));
-		assertEquals(a1.getBi8List(), Arrays.asList(b2));
-		assertEquals(a2.getBi8s(), Arrays.asList(b3));
-		assertEquals(a2.getBi8List(), Arrays.asList(b3));
-		assertNull(b1.getBi8());
-		assertSame(b2.getBi8(), a1);
-		assertSame(b3.getBi8(), a2);
-	}
-
-
-
-	/**
-	 * rel A.Bi9* <-> B.Bi9*;
-	 */
-	private void testBi9() {
-		setup();
-		a1.addBi9(b1);
-		a1.addBi9(b2);
-
-		assertEquals(a1.getBi9s(), Arrays.asList(b1, b2));
-		assertEquals(a1.getBi9List(), Arrays.asList(b1, b2));
-		assertEquals(a2.getBi9s(), Arrays.asList());
-		assertEquals(a2.getBi9List(), Arrays.asList());
-		assertEquals(a3.getBi9s(), Arrays.asList());
-		assertEquals(a3.getBi9List(), Arrays.asList());
-		assertEquals(b1.getBi9s(), Arrays.asList(a1));
-		assertEquals(b1.getBi9List(), Arrays.asList(a1));
-		assertEquals(b2.getBi9s(), Arrays.asList(a1));
-		assertEquals(b2.getBi9List(), Arrays.asList(a1));
-		assertEquals(b3.getBi9s(), Arrays.asList());
-		assertEquals(b3.getBi9List(), Arrays.asList());
-
-		b3.addBi9(a1);
-		b3.addBi9(a3);
-		b3.addBi9(a1);
-
-		assertEquals(a1.getBi9s(), Arrays.asList(b1, b2, b3, b3));
-		assertEquals(a1.getBi9List(), Arrays.asList(b1, b2, b3, b3));
-		assertEquals(a2.getBi9s(), Arrays.asList());
-		assertEquals(a2.getBi9List(), Arrays.asList());
-		assertEquals(a3.getBi9s(), Arrays.asList(b3));
-		assertEquals(a3.getBi9List(), Arrays.asList(b3));
-		assertEquals(b1.getBi9s(), Arrays.asList(a1));
-		assertEquals(b1.getBi9List(), Arrays.asList(a1));
-		assertEquals(b2.getBi9s(), Arrays.asList(a1));
-		assertEquals(b2.getBi9List(), Arrays.asList(a1));
-		assertEquals(b3.getBi9s(), Arrays.asList(a1, a3, a1));
-		assertEquals(b3.getBi9List(), Arrays.asList(a1, a3, a1));
-
-		b3.removeBi9(a1);
-
-		assertEquals(a1.getBi9s(), Arrays.asList(b1, b2, b3));
-		assertEquals(a1.getBi9List(), Arrays.asList(b1, b2, b3));
-		assertEquals(a2.getBi9s(), Arrays.asList());
-		assertEquals(a2.getBi9List(), Arrays.asList());
-		assertEquals(a3.getBi9s(), Arrays.asList(b3));
-		assertEquals(a3.getBi9List(), Arrays.asList(b3));
-		assertEquals(b1.getBi9s(), Arrays.asList(a1));
-		assertEquals(b1.getBi9List(), Arrays.asList(a1));
-		assertEquals(b2.getBi9s(), Arrays.asList(a1));
-		assertEquals(b2.getBi9List(), Arrays.asList(a1));
-		assertEquals(b3.getBi9s(), Arrays.asList(a3, a1));
-		assertEquals(b3.getBi9List(), Arrays.asList(a3, a1));
-	}
-
-
-	public void testImmutableList() {
-		setup();
-
-		a1.addDi3(b1);
-		a1.addDi3(b2);
-		try {
-			a1.getDi3s().add(b3);
-			assertException();
-		} catch (Exception e) {
-			// OK
-		}
-
-		a1.addBi7(b1);
-		a1.addBi7(b2);
-		try {
-			a1.getBi7s().add(b3);
-			assertException();
-		} catch (Exception e) {
-			// OK
-		}
-
-		a1.addBi9(b1);
-		a1.addBi9(b2);
-		try {
-			a1.getBi9s().add(b3);
-			assertException();
-		} catch (Exception e) {
-			// OK
-		}
-	}
-
-	private void setup() {
-		r = new Root();
-		a1 = new A("a1");
-		a2 = new A("a2");
-		a3 = new A("a3");
-		b1 = new B("b1");
-		b2 = new B("b2");
-		b3 = new B("b3");
-
-		r.addA(a1);
-		r.addA(a2);
-		r.addA(a3);
-		r.addB(b1);
-		r.addB(b2);
-		r.addB(b3);
-	}
-}
diff --git a/tests/valid/LowerBounds.java b/tests/valid/LowerBounds.java
deleted file mode 100644
index 2c8d0b2..0000000
--- a/tests/valid/LowerBounds.java
+++ /dev/null
@@ -1,47 +0,0 @@
-import AST.*;
-
-public class LowerBounds extends AbstractTests {
-	public static void main(String args[]) {
-		new LowerBounds().test();
-	}
-
-	/*
-	 * Root ::= A* B*;
-	 * A ::= <Name> [C];
-	 * B ::= <Name>;
-	 * C ::= <Name>;
-	 * rel A.Br -> B;
-	 * rel B.Cr <-> C.Br;
-	 * rel Root.Aa? -> A;
-	 */
-	public void test() {
-		Root r = new Root();
-		C c1 = new C("c1");
-		C c2 = new C("c2");
-		A a1 = new A("a1", new Opt<>(c1));
-		A a2 = new A("a2", new Opt<>(c2));
-		B b1 = new B("b1");
-		B b2 = new B("b2");
-		r.addA(a1);
-		r.addA(a2);
-		r.addB(b1);
-		r.addB(b2);
-
-		assertTrue(r.violatesLowerBounds());
-
-		a1.setBr(b1);
-		a2.setBr(b2);
-		b1.setCr(c1);
-		b2.setCr(c2);
-
-		assertFalse(r.violatesLowerBounds());
-
-		b2.setCr(c1);
-
-		assertTrue(r.violatesLowerBounds());
-
-		b1.setCr(c2);
-
-		assertFalse(r.violatesLowerBounds());
-	}
-}
diff --git a/tests/valid/Makefile b/tests/valid/Makefile
deleted file mode 100644
index e2368ea..0000000
--- a/tests/valid/Makefile
+++ /dev/null
@@ -1,22 +0,0 @@
-all: build-jar test
-test: test1 test2
-	@echo "#"
-	@echo "# VALID TESTS OK"
-	@echo "#"
-
-build-jar:
-	(cd ../../ && ./gradlew jar)
-test1:
-	java -jar ../../build/libs/relast.jar All.relast --file --grammarName=AllGen
-	rm -rf AST
-	java -jar ../../tools/jastadd2.jar --package=AST AllGen.ast AllGen.jadd Utils.jadd
-	javac AST/*.java All.java
-	java All
-	java -jar ../../build/libs/relast.jar AllGen.ast --file  --grammarName=AllGenGen
-	diff AllGen.ast AllGenGen.ast
-test2:
-	java -jar ../../build/libs/relast.jar LowerBounds.relast --file  --grammarName=LowerBoundsGen
-	rm -rf AST
-	java -jar ../../tools/jastadd2.jar --package=AST LowerBoundsGen.ast LowerBoundsGen.jadd Utils.jadd
-	javac AST/*.java LowerBounds.java
-	java LowerBounds
-- 
GitLab