Convert rest of compilationTests

Test: this is it.
Bug: 173134729
Change-Id: I8792771d0706d951fdf47c90e0f0eee015f5178e
diff --git a/compilationTests/BUILD.bazel b/compilationTests/BUILD.bazel
index 32a63df..c0ad20f 100644
--- a/compilationTests/BUILD.bazel
+++ b/compilationTests/BUILD.bazel
@@ -16,7 +16,7 @@
         "//tools/base/build-system:studio_repo.zip",
     ] + glob(["testData/**"]),
     test_resources = ["src/test/resources"],
-    test_shard_count = 2,
+    test_shard_count = 4,
     test_srcs = ["src/test/java"],
     test_timeout = "long",
     visibility = ["//visibility:public"],
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/RecursiveObservableTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/RecursiveObservableTest.kt
deleted file mode 100644
index 262960b..0000000
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/RecursiveObservableTest.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.databinding.compilationTest
-
-import android.databinding.tool.processing.ErrorMessages
-import org.hamcrest.CoreMatchers.containsString
-import org.hamcrest.MatcherAssert.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-@RunWith(JUnit4::class)
-class RecursiveObservableTest : BaseCompilationTest() {
-    @Test
-    fun recursiveObservableUsed() {
-        prepareProject()
-        copyResourceTo("/layout/recursive_layout.xml",
-                "/app/src/main/res/layout/recursive.xml")
-        copyResourceTo(
-                "/androidx/databinding/compilationTest/badJava/RecursiveLiveData.java",
-                "/app/src/main/java/androidx/databinding/compilationTest/badJava/RecursiveLiveData.java")
-        val result = runGradle("assembleDebug")
-        assertThat(result.error, result.bindingExceptions.firstOrNull()?.createHumanReadableMessage(),
-                containsString(
-                        String.format(ErrorMessages.RECURSIVE_OBSERVABLE, "recursiveLiveData.text")
-                ))
-    }
-}
\ No newline at end of file
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/TooManyLayoutsTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/TooManyLayoutsTest.kt
deleted file mode 100644
index 16a8690..0000000
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/TooManyLayoutsTest.kt
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package androidx.databinding.compilationTest
-
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-
-/**
- * Stress test that creates many layouts and hopes that we don't choke compiler
- */
-@RunWith(JUnit4::class)
-class TooManyLayoutsTest : BaseCompilationTest() {
-    @Test
-    fun tooManyLayouts() {
-        prepareProject()
-        // 3000 is a random number that does not hit the # of static references limit but still fairly large
-        // for any reasonable project
-        (0 until 3000).forEach {
-            copyResourceTo("/layout/basic_layout.xml",
-                    "/app/src/main/res/layout/layout_$it.xml")
-        }
-        val result = runGradle("assembleDebug")
-        assertEquals(result.error, 0, result.resultCode.toLong())
-    }
-
-    @Test
-    fun tooManyModules() {
-        val moduleCount = 5
-        // we can actually support more layouts per module but it causes a timeout in the build
-        // server because test takes a long time.
-        val layoutsPerModule = 25
-        val appDeps = (0 until moduleCount).map {
-            "implementation project(':module$it')"
-        }.joinToString(System.lineSeparator())
-        val settings = (0 until moduleCount).map {
-            "include ':module$it'"
-        }.joinToString(System.lineSeparator())
-        prepareApp(toMap(KEY_DEPENDENCIES, appDeps,
-                KEY_SETTINGS_INCLUDES, "include ':app'\n$settings"))
-        (0 until moduleCount).forEach {
-            prepareModule("module$it", "com.example.module$it", toMap(KEY_DEPENDENCIES,
-                ""))
-            (0 until layoutsPerModule).forEach { layoutId ->
-                copyResourceTo("/layout/basic_layout.xml",
-                        "/module$it/src/main/res/layout/module${it}_${layoutId}_layout.xml")
-            }
-
-        }
-        val result = runGradle("assembleDebug")
-        assertEquals(result.error, 0, result.resultCode)
-    }
-
-    @Test
-    fun tooManyModules_dependingOnEachother() {
-        val moduleCount = 5
-        val layoutsPerModule = 25
-        val appDeps = (0 until moduleCount).map {
-            "implementation project(':module0')"
-        }.joinToString(System.lineSeparator())
-        val settings = (0 until moduleCount).map {
-            "include ':module$it'"
-        }.joinToString(System.lineSeparator())
-        prepareApp(toMap(KEY_DEPENDENCIES, appDeps,
-                KEY_SETTINGS_INCLUDES, "include ':app'\n$settings"))
-        (0 until moduleCount).forEach {
-            val deps = if (it == 0) {
-                ""
-            } else {
-                "implementation project(':module${it-1}')"
-            }
-            prepareModule("module$it", "com.example.module$it", toMap(KEY_DEPENDENCIES,
-                    deps)) // add dependency ?
-            (0 until layoutsPerModule).forEach { layoutId ->
-                copyResourceTo("/layout/basic_layout.xml",
-                        "/module$it/src/main/res/layout/module${it}_${layoutId}_layout.xml")
-            }
-
-        }
-        val result = runGradle("assembleDebug")
-        assertEquals(result.error, 0, result.resultCode)
-    }
-}
\ No newline at end of file
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/AppCompatResourcesTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/AppCompatResourcesTest.kt
similarity index 66%
rename from compilationTests/src/test/java/androidx/databinding/compilationTest/AppCompatResourcesTest.kt
rename to compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/AppCompatResourcesTest.kt
index e6981d4..aab7e99 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/AppCompatResourcesTest.kt
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/AppCompatResourcesTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-package androidx.databinding.compilationTest
+package androidx.databinding.compilationTest.bazel
 
+import androidx.databinding.compilationTest.BaseCompilationTest.KEY_DEPENDENCIES
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.containsString
 import org.hamcrest.MatcherAssert.assertThat
@@ -30,7 +31,9 @@
  * `AppCompatResources` is found in the classpath or not.
  */
 @RunWith(Parameterized::class)
-class AppCompatResourcesTest(private val addAppCompatDependency: Boolean) : BaseCompilationTest() {
+class AppCompatResourcesTest(private val addAppCompatDependency: Boolean) :
+    DataBindingCompilationTestCase() {
+
     companion object {
         @JvmStatic
         @Parameterized.Parameters(name = "Add support dependency: {0}")
@@ -38,20 +41,23 @@
     }
 
     @Before
-    fun setUp() {
-        if (addAppCompatDependency) {
-            prepareApp(toMap(
-              KEY_DEPENDENCIES, "implementation 'androidx.appcompat:appcompat:1.0.0'"
-            ))
-        } else {
-            prepareProject()
-        }
-        copyResourceTo("/layout/layout_with_drawable.xml",
-          "/app/src/main/res/layout/layout_with_drawable.xml")
-        copyResourceTo("/drawable/thumbs_up.png",
-          "/app/src/main/res/drawable/thumbs_up.png")
+    fun setUpTest() {
+        val replacements = if (addAppCompatDependency) {
+            mapOf(
+                KEY_DEPENDENCIES to "implementation 'androidx.appcompat:appcompat:+'"
+            )
+        } else emptyMap()
+        loadApp(replacements)
+        copyTestData(
+            "layout/layout_with_drawable.xml",
+            "app/src/main/res/layout/layout_with_drawable.xml"
+        )
+        copyTestData(
+            "drawable/thumbs_up.png",
+            "app/src/main/res/drawable/thumbs_up.png"
+        )
 
-        val result = runGradle("assembleDebug")
+        val result = assembleDebug()
         assertThat(result.error, result.resultCode, `is`(0))
     }
 
@@ -68,8 +74,7 @@
         assertThat(bindingImpl.readText(Charsets.UTF_8), containsString(expectedCode))
     }
 
-    private fun findFile(fileName: String): File?
-      = testFolder.walkBottomUp().firstOrNull { it.name == fileName }
-
+    private fun findFile(fileName: String): File? =
+        projectRoot.walkBottomUp().firstOrNull { it.name == fileName }
 }
 
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestCase.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestCase.kt
index cb57e0f..62604ac 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestCase.kt
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestCase.kt
@@ -15,6 +15,8 @@
  */
 package androidx.databinding.compilationTest.bazel
 
+import android.databinding.tool.processing.ScopedErrorReport
+import android.databinding.tool.store.Location
 import androidx.databinding.compilationTest.BaseCompilationTest.DEFAULT_APP_PACKAGE
 import androidx.databinding.compilationTest.BaseCompilationTest.KEY_DEPENDENCIES
 import androidx.databinding.compilationTest.BaseCompilationTest.KEY_MANIFEST_PACKAGE
@@ -30,20 +32,24 @@
 import com.intellij.openapi.util.io.FileUtil.toSystemDependentName
 import com.intellij.util.io.createDirectories
 import com.intellij.util.io.readText
-import org.apache.commons.io.FileUtils
+import org.junit.Assert
 import java.io.File
+import java.io.IOException
 import java.nio.charset.StandardCharsets
 import java.nio.file.Files
 
-private const val TEST_DEPENDENCIES = "implementation 'androidx.appcompat:appcompat:+'\n" +
-        "implementation 'androidx.constraintlayout:constraintlayout:+'"
+private const val TEST_DEPENDENCIES = "implementation 'androidx.fragment:fragment:+'"
 private const val DEFAULT_SETTINGS_GRADLE = "include ':app'"
 
 private const val TEST_DATA_PATH = "tools/data-binding/compilationTests/testData"
 
 abstract class DataBindingCompilationTestCase : AndroidGradleTestCase() {
 
-    protected fun loadApp(appReplacements: Map<String, String> = emptyMap()) {
+    protected fun loadApp() {
+        loadApp(emptyMap())
+    }
+
+    protected fun loadApp(appReplacements: Map<String, String>) {
         loadProject(TestProjectPaths.DATA_BINDING_COMPILATION)
         projectRoot.toPath().resolve("app/src/main").createDirectories()
         val replacements = appendTestReplacements(appReplacements)
@@ -80,7 +86,7 @@
 
     protected fun assembleDebug() = invokeTasks(listOf("assembleDebug"))
 
-    protected fun invokeTasks(tasks: List<String>): CompilationResult {
+    protected fun invokeTasks(tasks: List<String>, args: List<String> = emptyList()): CompilationResult {
         val request =
             GradleBuildInvoker.Request(
                 project,
@@ -98,7 +104,7 @@
                 }
             }
         }
-        request.setCommandLineArguments(listOf("--offline"))
+        request.setCommandLineArguments(listOf("--offline") + args)
         val result = invokeGradle(project) { gradleInvoker ->
             gradleInvoker.executeTasks(request)
         }
@@ -179,4 +185,60 @@
         }
         return mutableMap
     }
+
+    /**
+     * Finds the error file referenced in the given error report.
+     * Handles possibly relative paths.
+     *
+     * Throws an assertion exception if the error file reported cannot be found.
+     */
+    protected fun requireErrorFile(report: ScopedErrorReport): File {
+        val path = report.filePath
+        Assert.assertNotNull(path)
+        var file = File(path)
+        if (file.exists()) {
+            return file
+        }
+        // might be relative, try in test project folder
+        file = File(projectRoot, path)
+        Assert.assertTrue("required error file is missing in " + file.absolutePath, file.exists())
+        return file
+    }
+
+    /**
+     * Extracts the text in the given location from the file at the given application path.
+     *
+     * @param relativePath the relative path of the file to be extracted from
+     * @param location  The location to extract
+     * @return The string that is contained in the given location
+     * @throws IOException If file is invalid.
+     */
+    protected fun extract(relativePath: String, location: Location): String {
+        val file = File(projectRoot, relativePath)
+        Assert.assertTrue(file.exists())
+        val result = StringBuilder()
+        val lines = file.readLines(StandardCharsets.UTF_8)
+        for (i in location.startLine..location.endLine) {
+            if (i > location.startLine) {
+                result.append("\n")
+            }
+            val line = lines[i]
+            var start = 0
+            if (i == location.startLine) {
+                start = location.startOffset
+            }
+            var end = line.length - 1 // inclusive
+            if (i == location.endLine) {
+                end = location.endOffset
+            }
+            result.append(line.substring(start, end + 1))
+        }
+        return result.toString()
+    }
+
+    protected fun writeFile(path: String, contents: String) {
+        val targetFile = File(projectRoot, path)
+        targetFile.parentFile.mkdirs()
+        targetFile.writeText(contents, StandardCharsets.UTF_8)
+    }
 }
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestSuite.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestSuite.kt
index b52bc39..759457e 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestSuite.kt
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/DataBindingCompilationTestSuite.kt
@@ -15,29 +15,12 @@
  */
 package androidx.databinding.compilationTest.bazel
 
-import androidx.databinding.compilationTest.AppCompatResourcesTest
-import androidx.databinding.compilationTest.InverseMethodTest
-import androidx.databinding.compilationTest.MultiLayoutVerificationTest
-import androidx.databinding.compilationTest.NonEnglishLocaleTest
-import androidx.databinding.compilationTest.ObservableGetDetectionTest
-import androidx.databinding.compilationTest.RecursiveObservableTest
-import androidx.databinding.compilationTest.TooManyLayoutsTest
 import com.android.testutils.JarTestSuiteRunner
 import com.android.tools.tests.IdeaTestSuiteBase
 import org.junit.runner.RunWith
 
 @RunWith(JarTestSuiteRunner::class)
-@JarTestSuiteRunner.ExcludeClasses(
-    DataBindingCompilationTestSuite::class,
-    //TODO(b/173134729): remove the tests below when they are migrated to bazel.
-    AppCompatResourcesTest::class,
-    InverseMethodTest::class,
-    MultiLayoutVerificationTest::class,
-    NonEnglishLocaleTest::class,
-    ObservableGetDetectionTest::class,
-    RecursiveObservableTest::class,
-    TooManyLayoutsTest::class
-)
+@JarTestSuiteRunner.ExcludeClasses(DataBindingCompilationTestSuite::class)
 class DataBindingCompilationTestSuite: IdeaTestSuiteBase() {
     companion object {
         init {
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/InverseMethodTest.java b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/InverseMethodTest.java
similarity index 73%
rename from compilationTests/src/test/java/androidx/databinding/compilationTest/InverseMethodTest.java
rename to compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/InverseMethodTest.java
index e35fe3c..6e11c32 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/InverseMethodTest.java
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/InverseMethodTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,24 +14,21 @@
  * limitations under the License.
  */
 
-package androidx.databinding.compilationTest;
+package androidx.databinding.compilationTest.bazel;
 
+import androidx.databinding.compilationTest.CompilationResult;
+import com.google.common.collect.Lists;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.junit.runners.Parameterized;
 
 import java.io.File;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
 @RunWith(JUnit4.class)
-public class InverseMethodTest extends BaseCompilationTest {
+public class InverseMethodTest extends DataBindingCompilationTestCase {
     @Test
     public void testInverseMethodWrongParameterType() throws Throwable {
         testErrorForMethod("InverseMethod_WrongParameterType", 23, "Could not find inverse " +
@@ -65,19 +62,24 @@
 
     private void testErrorForMethod(String className, int lineNumber, String expectedError)
             throws Throwable {
-        prepareProject();
-        copyResourceTo(
-                "/androidx/databinding/compilationTest/badJava/" + className + ".java",
-                "/app/src/main/java/androidx/databinding/compilationTest/badJava/" + className + ".java");
-        CompilationResult result = runGradle("assembleDebug", "--stacktrace");
+        loadApp();
+        copyTestData(
+                "androidx/databinding/compilationTests/badJava/" + className + ".java",
+                "app/src/main/java/androidx/databinding/compilationTest/badJava/"
+                + className
+                + ".java");
+        CompilationResult result = invokeTasks(Lists.newArrayList("assembleDebug"),
+                                               Lists.newArrayList("--stacktrace"));
         assertNotEquals(0, result.resultCode);
         String error = getErrorLine(result.error);
         assertNotNull("Couldn't find error in \n" + result.error, error);
-        File errorFile = new File(testFolder,
-                "/app/src/main/java/androidx/databinding/compilationTest/badJava/" +
-                        className +  ".java");
+        File errorFile = new File(getProjectRoot(),
+                                  "app/src/main/java/androidx/databinding/compilationTest/badJava/"
+                                  +
+                                  className
+                                  + ".java");
         assertEquals(errorFile.getCanonicalPath() + ":" + lineNumber + ": error: " + expectedError,
-                error);
+                     error);
     }
 
     private static String getErrorLine(String err) {
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/MultiLayoutVerificationTest.java b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/MultiLayoutVerificationTest.java
similarity index 65%
rename from compilationTests/src/test/java/androidx/databinding/compilationTest/MultiLayoutVerificationTest.java
rename to compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/MultiLayoutVerificationTest.java
index 138b9a7..53dd8f2 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/MultiLayoutVerificationTest.java
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/MultiLayoutVerificationTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,42 +14,43 @@
  * limitations under the License.
  */
 
-package androidx.databinding.compilationTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.junit.runners.Parameterized;
+package androidx.databinding.compilationTest.bazel;
 
 import android.databinding.tool.processing.ErrorMessages;
 import android.databinding.tool.processing.ScopedErrorReport;
 import android.databinding.tool.processing.ScopedException;
 import android.databinding.tool.store.Location;
+import androidx.databinding.compilationTest.CompilationResult;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
 
 import java.io.File;
 import java.io.IOException;
-import java.net.URISyntaxException;
+import java.util.Collections;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
+import static androidx.databinding.compilationTest.BaseCompilationTest.DEFAULT_APP_PACKAGE;
+import static androidx.databinding.compilationTest.BaseCompilationTest.KEY_CLASS_NAME;
+import static androidx.databinding.compilationTest.BaseCompilationTest.KEY_CLASS_TYPE;
+import static androidx.databinding.compilationTest.BaseCompilationTest.KEY_IMPORT_TYPE;
+import static androidx.databinding.compilationTest.BaseCompilationTest.KEY_INCLUDE_ID;
+import static androidx.databinding.compilationTest.BaseCompilationTest.KEY_VIEW_ID;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
 
 @RunWith(JUnit4.class)
-public class MultiLayoutVerificationTest extends BaseCompilationTest {
+public class MultiLayoutVerificationTest extends DataBindingCompilationTestCase {
     @Test
-    public void testMultipleLayoutFilesWithNameMismatch()
-            throws IOException, URISyntaxException, InterruptedException {
-        prepareProject();
-        copyResourceTo("/layout/layout_with_class_name.xml",
-                "/app/src/main/res/layout/with_class_name.xml", toMap(KEY_CLASS_NAME,
-                        "AClassName"));
-        copyResourceTo("/layout/layout_with_class_name.xml",
-                "/app/src/main/res/layout-land/with_class_name.xml", toMap(KEY_CLASS_NAME,
-                        "SomeOtherClassName"));
-        CompilationResult result = runGradle("assembleDebug");
+    public void testMultipleLayoutFilesWithNameMismatch() throws IOException {
+        loadApp();
+        copyTestDataWithReplacement("layout/layout_with_class_name.xml",
+                                    "app/src/main/res/layout/with_class_name.xml",
+                                    Collections.singletonMap(KEY_CLASS_NAME, "AClassName"));
+        copyTestDataWithReplacement("layout/layout_with_class_name.xml",
+                                    "app/src/main/res/layout-land/with_class_name.xml",
+                                    Collections.singletonMap(KEY_CLASS_NAME,
+                                                             "SomeOtherClassName"));
+        CompilationResult result = assembleDebug();
         assertNotEquals(result.output, 0, result.resultCode);
         List<ScopedException> exceptions = result.getBindingExceptions();
         assertEquals(result.error, 2, exceptions.size());
@@ -63,7 +64,7 @@
             Location location = report.getLocations().get(0);
             String name = file.getParentFile().getName();
             if ("layout".equals(name)) {
-                assertEquals(new File(testFolder,
+                assertEquals(new File(getProjectRoot(),
                         "/app/src/main/res/layout/with_class_name.xml")
                         .getCanonicalFile(), file.getCanonicalFile());
                 String extract = extract("/app/src/main/res/layout/with_class_name.xml",
@@ -75,7 +76,7 @@
                         "layout/with_class_name"), exception.getBareMessage());
                 foundNormal = true;
             } else if ("layout-land".equals(name)) {
-                    assertEquals(new File(testFolder,
+                    assertEquals(new File(getProjectRoot(),
                             "/app/src/main/res/layout-land/with_class_name.xml")
                             .getCanonicalFile(), file.getCanonicalFile());
                     String extract = extract("/app/src/main/res/layout-land/with_class_name.xml",
@@ -95,16 +96,16 @@
     }
 
     @Test
-    public void testMultipleLayoutFilesVariableMismatch()
-            throws IOException, URISyntaxException, InterruptedException {
-        prepareProject();
-        copyResourceTo("/layout/layout_with_variable_type.xml",
-                "/app/src/main/res/layout/layout_with_variable_type.xml", toMap(KEY_CLASS_TYPE,
-                        "String"));
-        copyResourceTo("/layout/layout_with_variable_type.xml",
-                "/app/src/main/res/layout-land/layout_with_variable_type.xml", toMap(KEY_CLASS_TYPE,
-                        "CharSequence"));
-        CompilationResult result = runGradle("assembleDebug");
+    public void testMultipleLayoutFilesVariableMismatch() throws IOException {
+        loadApp();
+        copyTestDataWithReplacement("layout/layout_with_variable_type.xml",
+                                    "app/src/main/res/layout/layout_with_variable_type.xml",
+                                    Collections.singletonMap(KEY_CLASS_TYPE, "String"));
+        copyTestDataWithReplacement("layout/layout_with_variable_type.xml",
+                                    "app/src/main/res/layout-land/layout_with_variable_type.xml",
+                                    Collections.singletonMap(KEY_CLASS_TYPE,
+                                                             "CharSequence"));
+        CompilationResult result = assembleDebug();
         assertNotEquals(result.output, 0, result.resultCode);
         List<ScopedException> exceptions = result.getBindingExceptions();
         assertEquals(result.error, 2, exceptions.size());
@@ -118,7 +119,6 @@
             Location location = report.getLocations().get(0);
             // validated in switch
             String name = file.getParentFile().getName();
-            String config = name;
             String type = "???";
             if ("layout".equals(name)) {
                 type = "String";
@@ -129,34 +129,35 @@
             } else {
                 fail("unexpected error file");
             }
-            assertEquals(new File(testFolder,
-                    "/app/src/main/res/" + config + "/layout_with_variable_type.xml")
+            assertEquals(new File(getProjectRoot(),
+                                  "/app/src/main/res/" + name + "/layout_with_variable_type.xml")
                     .getCanonicalFile(), file.getCanonicalFile());
-            String extract = extract("/app/src/main/res/" + config +
-                            "/layout_with_variable_type.xml", location);
+            String extract = extract("/app/src/main/res/" + name +
+                                     "/layout_with_variable_type.xml", location);
             assertEquals(extract, "<variable name=\"myVariable\" type=\"" + type + "\"/>");
             assertEquals(String.format(
                     ErrorMessages.MULTI_CONFIG_VARIABLE_TYPE_MISMATCH,
                     "myVariable", type,
-                    config + "/layout_with_variable_type"), exception.getBareMessage());
+                    name + "/layout_with_variable_type"), exception.getBareMessage());
         }
         assertTrue(result.error, foundNormal);
         assertTrue(result.error, foundLandscape);
     }
 
     @Test
-    public void testMultipleLayoutFilesImportMismatch()
-            throws IOException, URISyntaxException, InterruptedException {
-        prepareProject();
+    public void testMultipleLayoutFilesImportMismatch() throws IOException {
+        loadApp();
         String typeNormal = "java.util.List";
         String typeLand = "java.util.Map";
-        copyResourceTo("/layout/layout_with_import_type.xml",
-                "/app/src/main/res/layout/layout_with_import_type.xml", toMap(KEY_IMPORT_TYPE,
-                        typeNormal));
-        copyResourceTo("/layout/layout_with_import_type.xml",
-                "/app/src/main/res/layout-land/layout_with_import_type.xml", toMap(KEY_IMPORT_TYPE,
-                        typeLand));
-        CompilationResult result = runGradle("assembleDebug");
+        copyTestDataWithReplacement("layout/layout_with_import_type.xml",
+                                    "app/src/main/res/layout/layout_with_import_type.xml",
+                                    Collections.singletonMap(KEY_IMPORT_TYPE,
+                                                             typeNormal));
+        copyTestDataWithReplacement("layout/layout_with_import_type.xml",
+                                    "app/src/main/res/layout-land/layout_with_import_type.xml",
+                                    Collections.singletonMap(KEY_IMPORT_TYPE,
+                                                             typeLand));
+        CompilationResult result = assembleDebug();
         assertNotEquals(result.output, 0, result.resultCode);
         List<ScopedException> exceptions = result.getBindingExceptions();
         assertEquals(result.error, 2, exceptions.size());
@@ -170,7 +171,6 @@
             Location location = report.getLocations().get(0);
             // validated in switch
             String name = file.getParentFile().getName();
-            String config = name;
             String type = "???";
             if ("layout".equals(name)) {
                 type = typeNormal;
@@ -181,32 +181,33 @@
             } else {
                 fail("unexpected error file");
             }
-            assertEquals(new File(testFolder,
-                    "/app/src/main/res/" + config + "/layout_with_import_type.xml")
+            assertEquals(new File(getProjectRoot(),
+                                  "/app/src/main/res/" + name + "/layout_with_import_type.xml")
                     .getCanonicalFile(), file.getCanonicalFile());
-            String extract = extract("/app/src/main/res/" + config + "/layout_with_import_type.xml",
+            String extract = extract("/app/src/main/res/" + name + "/layout_with_import_type.xml",
                     location);
             assertEquals(extract, "<import alias=\"Blah\" type=\"" + type + "\"/>");
             assertEquals(String.format(
                     ErrorMessages.MULTI_CONFIG_IMPORT_TYPE_MISMATCH,
                     "Blah", type,
-                    config + "/layout_with_import_type"), exception.getBareMessage());
+                    name + "/layout_with_import_type"), exception.getBareMessage());
         }
         assertTrue(result.error, foundNormal);
         assertTrue(result.error, foundLandscape);
     }
 
     @Test
-    public void testSameIdInIncludeAndView()
-            throws IOException, URISyntaxException, InterruptedException {
-        prepareProject();
-        copyResourceTo("/layout/basic_layout.xml",
-                "/app/src/main/res/layout/basic_layout.xml");
-        copyResourceTo("/layout/layout_with_include.xml",
-                "/app/src/main/res/layout/foo.xml", toMap(KEY_INCLUDE_ID, "sharedId"));
-        copyResourceTo("/layout/layout_with_view_id.xml",
-                "/app/src/main/res/layout-land/foo.xml", toMap(KEY_VIEW_ID, "sharedId"));
-        CompilationResult result = runGradle("assembleDebug");
+    public void testSameIdInIncludeAndView() throws IOException {
+        loadApp();
+        copyTestData("layout/basic_layout.xml",
+                     "app/src/main/res/layout/basic_layout.xml");
+        copyTestDataWithReplacement("layout/layout_with_include.xml",
+                                    "app/src/main/res/layout/foo.xml",
+                                    Collections.singletonMap(KEY_INCLUDE_ID, "sharedId"));
+        copyTestDataWithReplacement("layout/layout_with_view_id.xml",
+                                    "app/src/main/res/layout-land/foo.xml",
+                                    Collections.singletonMap(KEY_VIEW_ID, "sharedId"));
+        CompilationResult result = assembleDebug();
         assertNotEquals(result.output, 0, result.resultCode);
         List<ScopedException> exceptions = result.getBindingExceptions();
 
@@ -237,7 +238,7 @@
             } else {
                 fail("unexpected error file");
             }
-            assertEquals(new File(testFolder,
+            assertEquals(new File(getProjectRoot(),
                     "/app/src/main/res/" + config + "/foo.xml").getCanonicalFile(),
                     file.getCanonicalFile());
             assertEquals(String.format(
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/NonEnglishLocaleTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/NonEnglishLocaleTest.kt
similarity index 71%
rename from compilationTests/src/test/java/androidx/databinding/compilationTest/NonEnglishLocaleTest.kt
rename to compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/NonEnglishLocaleTest.kt
index e01d489..3f05397 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/NonEnglishLocaleTest.kt
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/NonEnglishLocaleTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package androidx.databinding.compilationTest
+package androidx.databinding.compilationTest.bazel
 
 import org.hamcrest.CoreMatchers.`is`
 import org.hamcrest.CoreMatchers.containsString
@@ -23,17 +23,19 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
-import java.lang.AssertionError
 import java.util.Locale
 
 @RunWith(JUnit4::class)
-class NonEnglishLocaleTest : BaseCompilationTest() {
+class NonEnglishLocaleTest : DataBindingCompilationTestCase() {
     @Test
     fun turkishICapitalization() {
-        prepareProject()
-        copyResourceTo("/layout/turkish_i_capitalization.xml",
-                "/app/src/main/res/layout/i_turkish_capitalization.xml")
-        val result = runGradle("assembleDebug", "-Duser.language=tr", "-Duser.region=TR")
+        loadApp()
+        copyTestData(
+            "layout/turkish_i_capitalization.xml",
+            "app/src/main/res/layout/i_turkish_capitalization.xml"
+        )
+        val result =
+            invokeTasks(listOf("assembleDebug"), listOf("-Duser.language=tr", "-Duser.region=TR"))
         assertThat(result.error, result.resultCode, `is`(0))
         assertDoesNotContainTurkishChars("ITurkishCapitalizationBinding.java")
         assertDoesNotContainTurkishChars("ITurkishCapitalizationBindingImpl.java")
@@ -41,17 +43,18 @@
 
     @Test
     fun turkishICapitalization_twoWayBinding() {
-        prepareProject()
-        copyResourceTo("/layout/turkish_i_capitalization_two_way_binding.xml",
-                "/app/src/main/res/layout/i_turkish_capitalization.xml")
-        val result = runGradle("assembleDebug", "-Duser.language=tr", "-Duser.region=TR")
+        loadApp()
+        copyTestData("layout/turkish_i_capitalization_two_way_binding.xml",
+                     "app/src/main/res/layout/i_turkish_capitalization.xml")
+        val result =
+            invokeTasks(listOf("assembleDebug"), listOf("-Duser.language=tr", "-Duser.region=TR"))
         assertThat(result.error, result.resultCode, `is`(0))
         assertDoesNotContainTurkishChars("ITurkishCapitalizationBinding.java")
         assertDoesNotContainTurkishChars("ITurkishCapitalizationBindingImpl.java")
     }
 
     private fun assertDoesNotContainTurkishChars(fileName:String) {
-        val bindingJava = testFolder.walkBottomUp().firstOrNull {
+        val bindingJava = projectRoot.walkBottomUp().firstOrNull {
             it.name == fileName
         } ?: throw AssertionError("cannot find $fileName")
         // assert that it does not use turkish i
@@ -65,4 +68,4 @@
         // ı
         private val LOWERCASE_I = "I".toLowerCase(Locale.forLanguageTag("tr-TR"))
     }
-}
\ No newline at end of file
+}
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/ObservableGetDetectionTest.kt
similarity index 95%
rename from compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt
rename to compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/ObservableGetDetectionTest.kt
index 001410b..60bcee2 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/ObservableGetDetectionTest.kt
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/ObservableGetDetectionTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package androidx.databinding.compilationTest
+package androidx.databinding.compilationTest.bazel
 
 import org.apache.commons.lang3.StringEscapeUtils
 import org.hamcrest.CoreMatchers.`is`
@@ -28,13 +28,13 @@
         private val resolvedType: String, // e..g if it is ObservableInt, resolvedType is Int
         private val getter: String,
         private val constructor: String
-) : BaseCompilationTest() {
+) : DataBindingCompilationTestCase() {
     @Test
     fun detectGetterCallsOnObservables() {
         // this used to be disallowed on user code since 3.6 but causes issues w/ two way binding
         // which generates the same code. Now we instead support it but IDE will still show an
         // error to discourage developers.
-        prepareProject()
+        loadApp()
         // add an adapter so that it is settable on TextView
         writeFile("/app/src/main/java/com/example/MyAdapter.java",
                 """
@@ -60,13 +60,13 @@
                             android:text="@{myVariable.$getter}"/>
                 </layout>
                 """.trimIndent())
-        val result = runGradle("assembleDebug")
+        val result = assembleDebug()
         assertThat(result.error, result.resultCode, `is`(0))
     }
 
     @Test
     fun nestedObservable() {
-        prepareProject()
+        loadApp()
         writeFile("/app/src/main/java/com/example/MyClass.java",
                 """
                     package com.example;
@@ -88,13 +88,13 @@
                             android:text="@{``+myVariable.value}"/>
                 </layout>
                 """.trimIndent())
-        val result = runGradle("assembleDebug")
+        val result = assembleDebug()
         assertThat(result.error, result.resultCode, `is`(0))
     }
 
     @Test
     fun twoWayNested() {
-        prepareProject()
+        loadApp()
 
         writeFile("/app/src/main/java/com/example/MyClass.java",
                 """
@@ -125,7 +125,7 @@
                             android:text="@={MyClass.convertToString(myVariable.value)}"/>
                 </layout>
                 """.trimIndent())
-        val result = runGradle("assembleDebug")
+        val result = assembleDebug()
         assertThat(result.error, result.resultCode, `is`(0))
     }
 
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/RecursiveObservableTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/RecursiveObservableTest.kt
new file mode 100644
index 0000000..e7d6582
--- /dev/null
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/RecursiveObservableTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.databinding.compilationTest.bazel
+
+import android.databinding.tool.processing.ErrorMessages
+import org.hamcrest.CoreMatchers.containsString
+import org.hamcrest.MatcherAssert.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class RecursiveObservableTest : DataBindingCompilationTestCase() {
+
+    @Test
+    fun recursiveObservableUsed() {
+        loadApp()
+        copyTestData(
+            "layout/recursive_layout.xml",
+            "app/src/main/res/layout/recursive.xml"
+        )
+        copyTestData(
+            "androidx/databinding/compilationTests/badJava/RecursiveLiveData.java",
+            "app/src/main/java/androidx/databinding/compilationTest/badJava/RecursiveLiveData.java"
+        )
+        val result = assembleDebug()
+        assertThat(
+            result.error, result.bindingExceptions.firstOrNull()?.createHumanReadableMessage(),
+            containsString(
+                String.format(ErrorMessages.RECURSIVE_OBSERVABLE, "recursiveLiveData.text")
+            )
+        )
+    }
+}
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/SimpleCompilationTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/SimpleCompilationTest.kt
index d354cb5..0e8c369 100644
--- a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/SimpleCompilationTest.kt
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/SimpleCompilationTest.kt
@@ -16,9 +16,7 @@
 package androidx.databinding.compilationTest.bazel
 
 import android.databinding.tool.processing.ErrorMessages
-import android.databinding.tool.processing.ScopedErrorReport
 import android.databinding.tool.processing.ScopedException
-import android.databinding.tool.store.Location
 import androidx.databinding.compilationTest.BaseCompilationTest
 import androidx.databinding.compilationTest.BaseCompilationTest.KEY_DEPENDENCIES
 import androidx.databinding.compilationTest.BaseCompilationTest.KEY_MANIFEST_PACKAGE
@@ -35,7 +33,6 @@
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import java.io.File
-import java.io.IOException
 import java.nio.charset.StandardCharsets
 
 @RunWith(JUnit4::class)
@@ -570,54 +567,4 @@
         }
         return scopedException
     }
-
-    /**
-     * Extracts the text in the given location from the file at the given application path.
-     *
-     * @param relativePath the relative path of the file to be extracted from
-     * @param location  The location to extract
-     * @return The string that is contained in the given location
-     * @throws IOException If file is invalid.
-     */
-    private fun extract(relativePath: String, location: Location): String {
-        val file = File(projectRoot, relativePath)
-        Assert.assertTrue(file.exists())
-        val result = StringBuilder()
-        val lines = file.readLines(StandardCharsets.UTF_8)
-        for (i in location.startLine..location.endLine) {
-            if (i > location.startLine) {
-                result.append("\n")
-            }
-            val line = lines[i]
-            var start = 0
-            if (i == location.startLine) {
-                start = location.startOffset
-            }
-            var end = line.length - 1 // inclusive
-            if (i == location.endLine) {
-                end = location.endOffset
-            }
-            result.append(line.substring(start, end + 1))
-        }
-        return result.toString()
-    }
-
-    /**
-     * Finds the error file referenced in the given error report.
-     * Handles possibly relative paths.
-     *
-     * Throws an assertion exception if the error file reported cannot be found.
-     */
-    private fun requireErrorFile(report: ScopedErrorReport): File {
-        val path = report.filePath
-        Assert.assertNotNull(path)
-        var file = File(path)
-        if (file.exists()) {
-            return file
-        }
-        // might be relative, try in test project folder
-        file = File(projectRoot, path)
-        Assert.assertTrue("required error file is missing in " + file.absolutePath, file.exists())
-        return file
-    }
 }
diff --git a/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/TooManyLayoutsTest.kt b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/TooManyLayoutsTest.kt
new file mode 100644
index 0000000..4e2ffaa
--- /dev/null
+++ b/compilationTests/src/test/java/androidx/databinding/compilationTest/bazel/TooManyLayoutsTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.databinding.compilationTest.bazel
+
+import androidx.databinding.compilationTest.BaseCompilationTest.KEY_DEPENDENCIES
+import androidx.databinding.compilationTest.BaseCompilationTest.KEY_MANIFEST_PACKAGE
+import androidx.databinding.compilationTest.BaseCompilationTest.KEY_SETTINGS_INCLUDES
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+/**
+ * Stress test that creates many layouts and hopes that we don't choke compiler
+ */
+@RunWith(JUnit4::class)
+class TooManyLayoutsTest : DataBindingCompilationTestCase() {
+
+    @Test
+    fun tooManyLayouts() {
+        loadApp()
+        // 3000 is a random number that does not hit the # of static references limit but still fairly large
+        // for any reasonable project
+        (0 until 3000).forEach {
+            copyTestData(
+                "layout/basic_layout.xml",
+                "app/src/main/res/layout/layout_$it.xml"
+            )
+        }
+        val result = assembleDebug()
+        assertEquals(result.error, 0, result.resultCode.toLong())
+    }
+
+    @Test
+    fun tooManyModules() {
+        val moduleCount = 5
+        // we can actually support more layouts per module but it causes a timeout in the build
+        // server because test takes a long time.
+        val layoutsPerModule = 25
+        val appDeps = (0 until moduleCount).map {
+            "implementation project(':module$it')"
+        }.joinToString(System.lineSeparator())
+        val settings = (0 until moduleCount).map {
+            "include ':module$it'"
+        }.joinToString(System.lineSeparator())
+        loadApp(
+            mapOf(
+                KEY_DEPENDENCIES to appDeps,
+                KEY_SETTINGS_INCLUDES to "include ':app'\n$settings"
+            )
+        )
+        (0 until moduleCount).forEach {
+            loadModule("module$it", mapOf(KEY_MANIFEST_PACKAGE to "com.example.module$it"))
+            (0 until layoutsPerModule).forEach { layoutId ->
+                copyTestData(
+                    "layout/basic_layout.xml",
+                    "module$it/src/main/res/layout/module${it}_${layoutId}_layout.xml"
+                )
+            }
+
+        }
+        val result = assembleDebug()
+        assertEquals(result.error, 0, result.resultCode)
+    }
+
+    @Test
+    fun tooManyModules_dependingOnEachother() {
+        val moduleCount = 5
+        val layoutsPerModule = 25
+        val appDeps = (0 until moduleCount).map {
+            "implementation project(':module0')"
+        }.joinToString(System.lineSeparator())
+        val settings = (0 until moduleCount).map {
+            "include ':module$it'"
+        }.joinToString(System.lineSeparator())
+        loadApp(
+            mapOf(
+                KEY_DEPENDENCIES to appDeps,
+                KEY_SETTINGS_INCLUDES to "include ':app'\n$settings"
+            )
+        )
+        (0 until moduleCount).forEach {
+            val deps = if (it == 0) {
+                ""
+            } else {
+                "implementation project(':module${it - 1}')"
+            }
+            loadModule(
+                "module$it",
+                mapOf(
+                    KEY_MANIFEST_PACKAGE to "com.example.module$it",
+                    KEY_DEPENDENCIES to deps
+                )
+            ) // add dependency ?
+            (0 until layoutsPerModule).forEach { layoutId ->
+                copyTestData(
+                    "layout/basic_layout.xml",
+                    "module$it/src/main/res/layout/module${it}_${layoutId}_layout.xml"
+                )
+            }
+
+        }
+        val result = assembleDebug()
+        assertEquals(result.error, 0, result.resultCode)
+    }
+}
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_Void.java b/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_Void.java
deleted file mode 100644
index aa4bed8..0000000
--- a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_Void.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.databinding.compilationTest.badJava;
-
-import androidx.databinding.InverseMethod;
-
-public class InverseMethod_Void {
-    @InverseMethod("voidReturnInverse")
-    public static void voidReturn(int i) {}
-    public static String voidReturnInverse(int i) { return null; }
-}
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_WrongReturnType.java b/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_WrongReturnType.java
deleted file mode 100644
index bf0c696..0000000
--- a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_WrongReturnType.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.databinding.compilationTest.badJava;
-
-import androidx.databinding.InverseMethod;
-
-public class InverseMethod_WrongReturnType {
-    @InverseMethod("wrongReturnType")
-    public static int stringToInt(String s) { return 0; }
-    public static int wrongReturnType(int i) { return 0; }
-}
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NoArg.java b/compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NoArg.java
similarity index 100%
rename from compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NoArg.java
rename to compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NoArg.java
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NoInverse.java b/compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NoInverse.java
similarity index 100%
rename from compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NoInverse.java
rename to compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NoInverse.java
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NonPublic.java b/compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NonPublic.java
similarity index 100%
rename from compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NonPublic.java
rename to compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NonPublic.java
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NonPublicInverse.java b/compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NonPublicInverse.java
similarity index 100%
rename from compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_NonPublicInverse.java
rename to compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_NonPublicInverse.java
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_WrongParameterType.java b/compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_WrongParameterType.java
similarity index 100%
rename from compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/InverseMethod_WrongParameterType.java
rename to compilationTests/testData/androidx/databinding/compilationTests/badJava/InverseMethod_WrongParameterType.java
diff --git a/compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/RecursiveLiveData.java b/compilationTests/testData/androidx/databinding/compilationTests/badJava/RecursiveLiveData.java
similarity index 100%
rename from compilationTests/src/test/resources/androidx/databinding/compilationTest/badJava/RecursiveLiveData.java
rename to compilationTests/testData/androidx/databinding/compilationTests/badJava/RecursiveLiveData.java
diff --git a/compilationTests/src/test/resources/drawable/thumbs_up.png b/compilationTests/testData/drawable/thumbs_up.png
similarity index 100%
rename from compilationTests/src/test/resources/drawable/thumbs_up.png
rename to compilationTests/testData/drawable/thumbs_up.png
Binary files differ
diff --git a/compilationTests/src/test/resources/layout/layout_with_class_name.xml b/compilationTests/testData/layout/layout_with_class_name.xml
similarity index 100%
rename from compilationTests/src/test/resources/layout/layout_with_class_name.xml
rename to compilationTests/testData/layout/layout_with_class_name.xml
diff --git a/compilationTests/src/test/resources/layout/layout_with_drawable.xml b/compilationTests/testData/layout/layout_with_drawable.xml
similarity index 100%
rename from compilationTests/src/test/resources/layout/layout_with_drawable.xml
rename to compilationTests/testData/layout/layout_with_drawable.xml
diff --git a/compilationTests/src/test/resources/layout/layout_with_import_type.xml b/compilationTests/testData/layout/layout_with_import_type.xml
similarity index 100%
rename from compilationTests/src/test/resources/layout/layout_with_import_type.xml
rename to compilationTests/testData/layout/layout_with_import_type.xml
diff --git a/compilationTests/testData/layout/layout_with_include.xml b/compilationTests/testData/layout/layout_with_include.xml
new file mode 100644
index 0000000..2eaef57
--- /dev/null
+++ b/compilationTests/testData/layout/layout_with_include.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:bind="http://schemas.android.com/apk/res-auto">
+    <data>
+        <variable name="myVariable" type="String"/>
+    </data>
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+        <!-- undefined variable -->
+        <include layout="@layout/basic_layout" android:id="@+id/!@{INCLUDEID}" bind:myVariable="@{myVariable}"/>
+    </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/compilationTests/src/test/resources/layout/layout_with_variable_type.xml b/compilationTests/testData/layout/layout_with_variable_type.xml
similarity index 100%
rename from compilationTests/src/test/resources/layout/layout_with_variable_type.xml
rename to compilationTests/testData/layout/layout_with_variable_type.xml
diff --git a/compilationTests/testData/layout/layout_with_view_id.xml b/compilationTests/testData/layout/layout_with_view_id.xml
new file mode 100644
index 0000000..21dc440
--- /dev/null
+++ b/compilationTests/testData/layout/layout_with_view_id.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:bind="http://schemas.android.com/apk/res-auto">
+    <data>
+        <variable name="myVariable" type="String"/>
+    </data>
+    <LinearLayout
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+        <!-- undefined variable -->
+        <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/!@{VIEWID}" android:text="@{myVariable}"/>
+    </LinearLayout>
+</layout>
\ No newline at end of file
diff --git a/compilationTests/src/test/resources/layout/recursive_layout.xml b/compilationTests/testData/layout/recursive_layout.xml
similarity index 100%
rename from compilationTests/src/test/resources/layout/recursive_layout.xml
rename to compilationTests/testData/layout/recursive_layout.xml
diff --git a/compilationTests/src/test/resources/layout/turkish_i_capitalization.xml b/compilationTests/testData/layout/turkish_i_capitalization.xml
similarity index 100%
rename from compilationTests/src/test/resources/layout/turkish_i_capitalization.xml
rename to compilationTests/testData/layout/turkish_i_capitalization.xml
diff --git a/compilationTests/src/test/resources/layout/turkish_i_capitalization_two_way_binding.xml b/compilationTests/testData/layout/turkish_i_capitalization_two_way_binding.xml
similarity index 100%
rename from compilationTests/src/test/resources/layout/turkish_i_capitalization_two_way_binding.xml
rename to compilationTests/testData/layout/turkish_i_capitalization_two_way_binding.xml