Clean up code duplication in tests with gradleRunner

Test: ./gradlew room:int-test:room-inc-anno-proc:test
      ./gradlew nav:nav-safe-args-gr-plugin:test
      ./gradlew bench:bench-gradle-plugin:test

Change-Id: I13743a39854ca71fdb8b818a5229dda0d5f43fc5
diff --git a/benchmark/gradle-plugin/build.gradle b/benchmark/gradle-plugin/build.gradle
index c794a45..43ebd26 100644
--- a/benchmark/gradle-plugin/build.gradle
+++ b/benchmark/gradle-plugin/build.gradle
@@ -38,6 +38,7 @@
     implementation(KOTLIN_STDLIB)
 
     testImplementation gradleTestKit()
+    testImplementation(project(":internal-testutils-gradle-plugin"))
     testImplementation(ANDROIDX_TEST_RUNNER)
     testImplementation(JUNIT)
     testImplementation(KOTLIN_TEST)
diff --git a/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt b/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
index 7844207..5d04073 100644
--- a/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
+++ b/benchmark/gradle-plugin/src/test/kotlin/androidx/benchmark/gradle/BenchmarkPluginTest.kt
@@ -16,95 +16,56 @@
 
 package androidx.benchmark.gradle
 
+import androidx.testutils.gradle.ProjectSetupRule
 import org.gradle.testkit.runner.GradleRunner
 import org.gradle.testkit.runner.UnexpectedBuildFailure
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
-import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
 import org.junit.runners.JUnit4
 import java.io.File
-import java.util.Properties
 import kotlin.test.assertFailsWith
 import kotlin.test.assertFalse
 import kotlin.test.assertTrue
 
+private val PLUGINS_HEADER = """
+    plugins {
+        id('androidx.benchmark')
+        id('com.android.library')
+    }
+""".trimIndent()
+
 @RunWith(JUnit4::class)
 class BenchmarkPluginTest {
-
     @get:Rule
-    val testProjectDir = TemporaryFolder()
+    val projectSetup = ProjectSetupRule()
 
-    private lateinit var buildToolsVersion: String
-    private lateinit var compileSdkVersion: String
-    private lateinit var prebuiltsRoot: String
-    private lateinit var minSdkVersion: String
-
-    private lateinit var buildFile: File
-    private lateinit var propertiesFile: File
     private lateinit var versionPropertiesFile: File
     private lateinit var gradleRunner: GradleRunner
 
     @Before
     fun setUp() {
-        val stream = BenchmarkPluginTest::class.java.classLoader.getResourceAsStream("sdk.prop")
-        val properties = Properties()
-        properties.load(stream)
-        prebuiltsRoot = properties.getProperty("prebuiltsRoot")
-        compileSdkVersion = properties.getProperty("compileSdkVersion")
-        buildToolsVersion = properties.getProperty("buildToolsVersion")
-        minSdkVersion = properties.getProperty("minSdkVersion")
-
-        testProjectDir.root.mkdirs()
-
-        val localPropFile = File("../../local.properties")
-        localPropFile.copyTo(File(testProjectDir.root, "local.properties"), overwrite = true)
-
-        buildFile = File(testProjectDir.root, "build.gradle")
-        buildFile.createNewFile()
-
-        propertiesFile = File(testProjectDir.root, "gradle.properties")
-        propertiesFile.writer().use {
-            val props = Properties()
-            props.setProperty("android.useAndroidX", "true")
-            props.setProperty("android.enableJetpack", "true")
-            props.store(it, null)
-        }
-
-        versionPropertiesFile = File(testProjectDir.root, "version.properties")
+        versionPropertiesFile = File(projectSetup.rootDir, "version.properties")
         versionPropertiesFile.createNewFile()
 
-        File("src/test/test-data", "app-project").copyRecursively(testProjectDir.root)
+        File("src/test/test-data", "app-project").copyRecursively(projectSetup.rootDir)
 
         gradleRunner = GradleRunner.create()
-            .withProjectDir(testProjectDir.root)
+            .withProjectDir(projectSetup.rootDir)
             .withPluginClasspath()
     }
 
     @Test
     fun applyPluginAppProject() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.application')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('androidx.benchmark')
                 }
-            }
-
+            """.trimIndent(),
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
@@ -118,27 +79,14 @@
 
     @Test
     fun applyPluginAndroidLibProject() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.library')
+                    id('androidx.benchmark')
                 }
-            }
-
+            """.trimIndent(),
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
@@ -152,9 +100,11 @@
 
     @Test
     fun applyPluginNonAndroidProject() {
-        buildFile.writeText(
+        val prebuiltsRoot = projectSetup.props.prebuiltsRoot
+        projectSetup.buildFile.writeText(
             """
             plugins {
+                id('java')
                 id('androidx.benchmark')
             }
 
@@ -163,49 +113,22 @@
                 maven { url "$prebuiltsRoot/androidx/internal" }
             }
 
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-
             dependencies {
-                androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
+                testImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
         """.trimIndent()
         )
 
         assertFailsWith(UnexpectedBuildFailure::class) {
-            gradleRunner.withArguments("assemble").build()
+            gradleRunner.withArguments("jar").build()
         }
     }
 
     @Test
     fun applyPluginNonBenchmarkProject() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-        """.trimIndent()
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = ""
         )
 
         val output = gradleRunner.withArguments("tasks").build()
@@ -215,27 +138,9 @@
 
     @Test
     fun applyPluginBeforeAndroid() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('androidx.benchmark')
-                id('com.android.library')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
             }
@@ -249,24 +154,11 @@
 
     @Test
     fun applyPluginOnAgp36() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('androidx.benchmark')
-                id('com.android.library')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunnerArguments additionalTestOutputDir: "/fake_path/files"
                 }
             }
@@ -281,7 +173,7 @@
         """.trimIndent()
         )
 
-        propertiesFile.appendText("android.enableAdditionalTestOutput=true")
+        projectSetup.gradlePropertiesFile.appendText("android.enableAdditionalTestOutput=true")
         versionPropertiesFile.writeText("buildVersion=3.6.0-alpha05")
 
         val output = gradleRunner.withArguments("tasks").build()
@@ -297,31 +189,17 @@
 
     @Test
     fun applyPluginOnAgp35() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('androidx.benchmark')
-                id('com.android.library')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunnerArguments.remove("additionalTestOutputDir")
                 }
             }
 
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
-
             }
 
             tasks.register("printInstrumentationArgs") {
@@ -331,7 +209,7 @@
             tasks.register("printTestBuildType") {
                 println android.testBuildType
             }
-        """.trimIndent()
+            """.trimIndent()
         )
 
         versionPropertiesFile.writeText("buildVersion=3.5.0-rc03")
@@ -352,32 +230,11 @@
 
     @Test
     fun applyPluginDefaultAgpProperties() {
-        buildFile.writeText(
-            """
-            import com.android.build.gradle.TestedExtension
-
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = "import com.android.build.gradle.TestedExtension\n$PLUGINS_HEADER",
+            suffix = """
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
-
             }
 
             tasks.register("printTestInstrumentationRunner") {
@@ -388,7 +245,7 @@
                 def extension = project.extensions.getByType(TestedExtension)
                 println extension.buildTypes.getByName("debug").testCoverageEnabled
             }
-        """.trimIndent()
+            """.trimIndent()
         )
 
         val runnerOutput = gradleRunner.withArguments("printTestInstrumentationRunner").build()
@@ -402,26 +259,11 @@
 
     @Test
     fun applyPluginOverrideAgpProperties() {
-        buildFile.writeText(
-            """
-            import com.android.build.gradle.TestedExtension
-
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = "import com.android.build.gradle.TestedExtension\n$PLUGINS_HEADER",
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
                 }
 
@@ -434,7 +276,6 @@
 
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha01"
-
             }
 
             tasks.register("printTestInstrumentationRunner") {
@@ -459,24 +300,11 @@
 
     @Test
     fun applyPluginAndroidOldRunner36() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunner "androidx.benchmark.AndroidBenchmarkRunner"
                     testInstrumentationRunnerArguments additionalTestOutputDir: "/fake_path/files"
                 }
@@ -485,10 +313,9 @@
             dependencies {
                 androidTestImplementation "androidx.benchmark:benchmark:1.0.0-alpha04"
             }
-        """.trimIndent()
+            """.trimIndent()
         )
-
-        propertiesFile.appendText("android.enableAdditionalTestOutput=true")
+        projectSetup.gradlePropertiesFile.appendText("android.enableAdditionalTestOutput=true")
 
         assertFailsWith(UnexpectedBuildFailure::class) {
             gradleRunner.withArguments("assemble").build()
@@ -497,24 +324,11 @@
 
     @Test
     fun applyPluginAndroidOldRunner35() {
-        buildFile.writeText(
-            """
-            plugins {
-                id('com.android.library')
-                id('androidx.benchmark')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = PLUGINS_HEADER,
+            suffix = """
             android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
                 defaultConfig {
-                    minSdkVersion $minSdkVersion
                     testInstrumentationRunner "androidx.benchmark.AndroidBenchmarkRunner"
                     testInstrumentationRunnerArguments.remove("additionalTestOutputDir")
                 }
diff --git a/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt b/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
index 5defbc6..c2ebd63 100644
--- a/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
+++ b/buildSrc/src/main/kotlin/androidx/build/SdkResourceGenerator.kt
@@ -59,6 +59,9 @@
     val kotlinStdlib: String = KOTLIN_STDLIB
 
     @get:Input
+    val rootProjectPath: String = project.rootProject.rootDir.absolutePath
+
+    @get:Input
     lateinit var gradleVersion: String
 
     @get:OutputFile
@@ -77,6 +80,7 @@
         writer.write("navigationCommon=$navigationCommon\n")
         writer.write("kotlinStdlib=$kotlinStdlib\n")
         writer.write("gradleVersion=$gradleVersion\n")
+        writer.write("rootProjectPath=$rootProjectPath\n")
         writer.close()
     }
 
diff --git a/navigation/navigation-safe-args-gradle-plugin/build.gradle b/navigation/navigation-safe-args-gradle-plugin/build.gradle
index b5bc1706..7d62068 100644
--- a/navigation/navigation-safe-args-gradle-plugin/build.gradle
+++ b/navigation/navigation-safe-args-gradle-plugin/build.gradle
@@ -35,6 +35,7 @@
     api(gradleApi())
     implementation(GSON)
     testImplementation(gradleTestKit())
+    testImplementation(project(":internal-testutils-gradle-plugin"))
     testImplementation(JUNIT)
 }
 
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
index fc9ef2a..9b3c06c 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/BasePluginTest.kt
@@ -16,17 +16,14 @@
 
 package androidx.navigation.safeargs.gradle
 
+import androidx.testutils.gradle.ProjectSetupRule
 import org.gradle.testkit.runner.BuildResult
 import org.gradle.testkit.runner.GradleRunner
 import org.gradle.testkit.runner.TaskOutcome
 import org.hamcrest.CoreMatchers
-import org.hamcrest.MatcherAssert
 import org.hamcrest.MatcherAssert.assertThat
-import org.junit.Before
 import org.junit.Rule
-import org.junit.rules.TemporaryFolder
 import java.io.File
-import java.util.Properties
 
 internal const val MAIN_DIR = "androidx/navigation/testapp"
 
@@ -46,20 +43,10 @@
 internal const val SEC = 1000L
 
 abstract class BasePluginTest {
-    @Suppress("MemberVisibilityCanBePrivate")
     @get:Rule
-    val testProjectDir = TemporaryFolder()
+    val projectSetup = ProjectSetupRule()
 
-    internal var buildFile: File = File("")
-    internal var prebuiltsRoot = ""
-    internal var compileSdkVersion = ""
-    internal var buildToolsVersion = ""
-    internal var minSdkVersion = ""
-    internal var debugKeystore = ""
-    internal var navigationCommon = ""
-    internal var kotlinStblib = ""
-
-    internal fun projectRoot(): File = testProjectDir.root
+    internal fun projectRoot(): File = projectSetup.rootDir
 
     internal fun assertGenerated(name: String, prefix: String? = null): File {
         return prefix?.let { assertExists(name, true, it) } ?: assertExists(name, true)
@@ -74,7 +61,7 @@
             projectRoot(),
             "${prefix}build/$GENERATED_PATH/$name"
         )
-        MatcherAssert.assertThat(
+        assertThat(
             generatedFile.exists(),
             CoreMatchers.`is`(ex)
         )
@@ -90,92 +77,40 @@
     internal fun runGradle(vararg args: String) = gradleBuilder(*args).build()
     internal fun runAndFailGradle(vararg args: String) = gradleBuilder(*args).buildAndFail()
 
-    @Before
-    fun setup() {
-        projectRoot().mkdirs()
-        buildFile = File(projectRoot(), "build.gradle")
-        buildFile.createNewFile()
-        // copy local.properties
-        val appToolkitProperties = File("../../local.properties")
-        if (appToolkitProperties.exists()) {
-            appToolkitProperties.copyTo(
-                File(projectRoot(), "local.properties"),
-                overwrite = true
-            )
-        } else {
-            File("../../local.properties").copyTo(
-                File(projectRoot(), "local.properties"), overwrite = true
-            )
-        }
-        val stream = BasePluginTest::class.java.classLoader.getResourceAsStream("sdk.prop")
-        val properties = Properties()
-        properties.load(stream)
-        prebuiltsRoot = properties.getProperty("prebuiltsRoot")
-        compileSdkVersion = properties.getProperty("compileSdkVersion")
-        buildToolsVersion = properties.getProperty("buildToolsVersion")
-        minSdkVersion = properties.getProperty("minSdkVersion")
-        debugKeystore = properties.getProperty("debugKeystore")
-        navigationCommon = properties.getProperty("navigationCommon")
-        kotlinStblib = properties.getProperty("kotlinStdlib")
-
-        val propertiesFile = File(projectRoot(), "gradle.properties")
-        propertiesFile.writer().use {
-            val props = Properties()
-            props.setProperty("android.useAndroidX", "true")
-            props.store(it, null)
-        }
-    }
-
     internal fun setupSimpleBuildGradle() {
         testData("app-project").copyRecursively(projectRoot())
-        buildFile.writeText("""
-            plugins {
-                id('com.android.application')
-                id('androidx.navigation.safeargs')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('androidx.navigation.safeargs')
                 }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
-                    }
+            """.trimIndent(),
+            suffix = """
+                dependencies {
+                    implementation "${projectSetup.props.navigationCommon}"
                 }
-            }
-
-            dependencies {
-                implementation "$navigationCommon"
-            }
-        """.trimIndent()
+            """.trimIndent()
         )
     }
 
     internal fun setupMultiModuleBuildGradle() {
         testData("multimodule-project").copyRecursively(projectRoot())
-        buildFile.writeText("""
+        val props = projectSetup.props
+        projectSetup.buildFile.writeText(
+            """
             buildscript {
-                ext.compileSdk = $compileSdkVersion
-                ext.buildTools = "$buildToolsVersion"
-                ext.minSdk = $minSdkVersion
-                ext.debugKeystoreFile = "$debugKeystore"
-                ext.navigationCommonDep = "$navigationCommon"
+                ext.compileSdk = ${props.compileSdkVersion}
+                ext.buildTools = "${props.buildToolsVersion}"
+                ext.minSdk = ${props.minSdkVersion}
+                ext.debugKeystoreFile = "${props.debugKeystore}"
+                ext.navigationCommonDep = "${props.navigationCommon}"
             }
 
             allprojects {
                 repositories {
-                    maven { url "$prebuiltsRoot/androidx/external" }
-                    maven { url "$prebuiltsRoot/androidx/internal" }
+                    maven { url "${props.prebuiltsRoot}/androidx/external" }
+                    maven { url "${props.prebuiltsRoot}/androidx/internal" }
                 }
             }
         """.trimIndent()
@@ -184,38 +119,20 @@
 
     internal fun setupSimpleKotlinBuildGradle() {
         testData("app-project-kotlin").copyRecursively(projectRoot())
-        buildFile.writeText("""
-            plugins {
-                id('com.android.application')
-                id('kotlin-android')
-                id('androidx.navigation.safeargs.kotlin')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('kotlin-android')
+                    id('androidx.navigation.safeargs.kotlin')
                 }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
-                    }
+            """.trimIndent(),
+            suffix = """
+                dependencies {
+                    implementation "${projectSetup.props.kotlinStblib}"
+                    implementation "${projectSetup.props.navigationCommon}"
                 }
-            }
-
-            dependencies {
-                implementation "$kotlinStblib"
-                implementation "$navigationCommon"
-            }
-        """.trimIndent()
+            """.trimIndent()
         )
     }
 }
diff --git a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt
index 114186e..9398dcdb4 100644
--- a/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt
+++ b/navigation/navigation-safe-args-gradle-plugin/src/test/kotlin/androidx/navigation/safeargs/gradle/JavaPluginTest.kt
@@ -27,49 +27,32 @@
     @Test
     fun runGenerateTask() {
         testData("app-project").copyRecursively(projectRoot())
-        buildFile.writeText("""
-            plugins {
-                id('com.android.application')
-                id('androidx.navigation.safeargs')
-            }
-
-            repositories {
-                maven { url "$prebuiltsRoot/androidx/external" }
-                maven { url "$prebuiltsRoot/androidx/internal" }
-            }
-
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-                flavorDimensions "mode"
-                productFlavors {
-                    foo {
-                        dimension "mode"
-                        applicationIdSuffix ".foo"
-                    }
-                    notfoo {
-                        dimension "mode"
-                    }
-
+        projectSetup.writeDefaultBuildGradle(
+            prefix = """
+                plugins {
+                    id('com.android.application')
+                    id('androidx.navigation.safeargs')
                 }
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
+            """.trimIndent(),
+            suffix = """
+                android {
+                    flavorDimensions "mode"
+                    productFlavors {
+                        foo {
+                            dimension "mode"
+                            applicationIdSuffix ".foo"
+                        }
+                        notfoo {
+                            dimension "mode"
+                        }
                     }
                 }
-            }
 
-            dependencies {
-                implementation "$navigationCommon"
-            }
-        """.trimIndent()
+                dependencies {
+                    implementation "${projectSetup.props.navigationCommon}"
+                }
+            """.trimIndent()
         )
-
         runGradle("assembleNotfooDebug", "assembleFooDebug")
             .assertSuccessfulTask("assembleNotfooDebug")
             .assertSuccessfulTask("assembleFooDebug")
diff --git a/room/integration-tests/incremental-annotation-processing/build.gradle b/room/integration-tests/incremental-annotation-processing/build.gradle
index 6139835..acc978a 100644
--- a/room/integration-tests/incremental-annotation-processing/build.gradle
+++ b/room/integration-tests/incremental-annotation-processing/build.gradle
@@ -25,6 +25,8 @@
 
 dependencies {
     implementation(KOTLIN_STDLIB)
+
+    testImplementation(project(":internal-testutils-gradle-plugin"))
     testImplementation(JUNIT)
     testImplementation(TRUTH)
     testImplementation gradleTestKit()
diff --git a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
index d9fd1da..1bf80e6 100644
--- a/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
+++ b/room/integration-tests/incremental-annotation-processing/src/test/kotlin/androidx/room/gradle/RoomIncrementalAnnotationProcessingTest.kt
@@ -16,6 +16,7 @@
 
 package androidx.room.gradle
 
+import androidx.testutils.gradle.ProjectSetupRule
 import com.google.common.truth.Expect
 import org.gradle.testkit.runner.BuildResult
 import org.gradle.testkit.runner.GradleRunner
@@ -23,12 +24,10 @@
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
-import org.junit.rules.TemporaryFolder
 import org.junit.runner.RunWith
 import org.junit.runners.Parameterized
 import java.io.File
 import java.nio.file.Files
-import java.util.Properties
 
 @RunWith(Parameterized::class)
 class RoomIncrementalAnnotationProcessingTest(private val withIncrementalRoom: Boolean) {
@@ -49,20 +48,11 @@
     }
 
     @get:Rule
-    val testProjectDir = TemporaryFolder()
+    val projectSetup = ProjectSetupRule()
 
     @get:Rule
     val expect: Expect = Expect.create()
 
-    // Properties to set up test project
-    private lateinit var prebuiltsRoot: String
-    private lateinit var agpDependency: String
-    private lateinit var localSupportRepo: String
-    private lateinit var compileSdkVersion: String
-    private lateinit var buildToolsVersion: String
-    private lateinit var minSdkVersion: String
-    private lateinit var debugKeystore: String
-
     // Original source files
     private lateinit var srcDatabase1: File
     private lateinit var srcDao1: File
@@ -100,34 +90,14 @@
 
     @Before
     fun setup() {
-        val projectRoot = testProjectDir.root
-
-        // copy local.properties
-        File("../../../local.properties")
-            .copyTo(File(projectRoot, "local.properties"), overwrite = true)
-
-        // copy sdk.prop (created by module's build.gradle)
-        RoomIncrementalAnnotationProcessingTest::class.java.classLoader
-            .getResourceAsStream("sdk.prop")
-            .use { input ->
-                val properties = Properties().apply { load(input) }
-                prebuiltsRoot = properties.getProperty("prebuiltsRoot")
-                localSupportRepo = properties.getProperty("localSupportRepo")
-                agpDependency = properties.getProperty("agpDependency")
-                compileSdkVersion = properties.getProperty("compileSdkVersion")
-                buildToolsVersion = properties.getProperty("buildToolsVersion")
-                minSdkVersion = properties.getProperty("minSdkVersion")
-                debugKeystore = properties.getProperty("debugKeystore")
-            }
+        val projectRoot = projectSetup.rootDir
+        val prebuiltsRoot = projectSetup.props.prebuiltsRoot
+        val localSupportRepo = projectSetup.props.localSupportRepo
+        val agpDependency = projectSetup.props.agpDependency
 
         // copy test project
         File("src/test/data/simple-project").copyRecursively(projectRoot)
 
-        // setup gradle.properties
-        File(projectRoot, "gradle.properties").writeText(
-            "android.useAndroidX=true"
-        )
-
         // set up build file
         File(projectRoot, "build.gradle").writeText(
             """
@@ -154,20 +124,7 @@
                 }
             }
 
-            android {
-                compileSdkVersion $compileSdkVersion
-                buildToolsVersion "$buildToolsVersion"
-
-                defaultConfig {
-                    minSdkVersion $minSdkVersion
-                }
-
-                signingConfigs {
-                    debug {
-                        storeFile file("$debugKeystore")
-                    }
-                }
-            }
+            %s
 
             dependencies {
                 // Uses latest Room built from tip of tree
@@ -200,7 +157,11 @@
                     }
                 }
             }
-        """.trimIndent()
+        """
+                .trimIndent()
+                // doing format instead of "$projectSetup.androidProject" on purpose,
+                // because otherwise trimIndent will mess with formatting
+                .format(projectSetup.androidProject)
         )
 
         // Compute file paths
@@ -230,7 +191,7 @@
 
     private fun runGradleTasks(vararg args: String): BuildResult {
         return GradleRunner.create()
-            .withProjectDir(testProjectDir.root)
+            .withProjectDir(projectSetup.rootDir)
             .withArguments(*args)
             .build()
     }
diff --git a/settings.gradle b/settings.gradle
index 7a80011..6b569df 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -322,6 +322,7 @@
 includeProject(":internal-testutils-truth", "testutils/testutils-truth")
 includeProject(":internal-testutils-ktx", "testutils/testutils-ktx")
 includeProject(":internal-testutils-navigation", "testutils/testutils-navigation")
+includeProject(":internal-testutils-gradle-plugin", "testutils/testutils-gradle-plugin")
 
 /////////////////////////////
 //
diff --git a/testutils/testutils-gradle-plugin/build.gradle b/testutils/testutils-gradle-plugin/build.gradle
new file mode 100644
index 0000000..43650cb
--- /dev/null
+++ b/testutils/testutils-gradle-plugin/build.gradle
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 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.
+ */
+
+import static androidx.build.dependencies.DependenciesKt.*
+
+plugins {
+    id("AndroidXPlugin")
+    id("kotlin")
+}
+
+dependencies {
+    implementation(KOTLIN_STDLIB)
+    implementation(ANDROIDX_TEST_EXT_JUNIT)
+    implementation(ANDROIDX_TEST_CORE)
+    implementation(ANDROIDX_TEST_RULES)
+}
+
+androidx {
+    toolingProject = true
+}
diff --git a/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
new file mode 100644
index 0000000..8fcd36a
--- /dev/null
+++ b/testutils/testutils-gradle-plugin/src/main/java/androidx/testutils/gradle/ProjectSetupRule.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2020 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.testutils.gradle
+
+import org.junit.rules.ExternalResource
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import java.io.File
+import java.lang.IllegalStateException
+import java.util.Properties
+
+/**
+ * Test rule that helps to setup android project in tests that run gradle.
+ *
+ * It should be used along side with SdkResourceGenerator in your build.gradle file
+ */
+class ProjectSetupRule : ExternalResource() {
+    val testProjectDir = TemporaryFolder()
+
+    lateinit var props: ProjectProps
+
+    val rootDir: File
+        get() = testProjectDir.root
+
+    val buildFile: File
+        get() = File(rootDir, "build.gradle")
+
+    val gradlePropertiesFile: File
+        get() = File(rootDir, "gradle.properties")
+
+    private val repositories: String
+        get() = """
+            repositories {
+                maven { url "${props.prebuiltsRoot}/androidx/external" }
+                maven { url "${props.prebuiltsRoot}/androidx/internal" }
+            }
+        """.trimIndent()
+
+    val androidProject: String
+        get() = """
+            android {
+                compileSdkVersion ${props.compileSdkVersion}
+                buildToolsVersion "${props.buildToolsVersion}"
+
+                defaultConfig {
+                    minSdkVersion ${props.minSdkVersion}
+                }
+            }
+        """.trimIndent()
+
+    private val defaultBuildGradle: String
+        get() = "\n$repositories\n\n$androidProject\n\n"
+
+    fun writeDefaultBuildGradle(prefix: String, suffix: String) {
+        buildFile.writeText(prefix)
+        buildFile.appendText(defaultBuildGradle)
+        buildFile.appendText(suffix)
+
+        println(buildFile.readText())
+    }
+
+    override fun apply(base: Statement, description: Description): Statement {
+        return testProjectDir.apply(super.apply(base, description), description)
+    }
+
+    override fun before() {
+        props = ProjectProps.load()
+        buildFile.createNewFile()
+        copyLocalProperties()
+        writeGradleProperties()
+    }
+
+    private fun copyLocalProperties() {
+        val localProperties = File(props.rootProjectPath, "local.properties")
+        if (localProperties.exists()) {
+            localProperties.copyTo(File(rootDir, "local.properties"), overwrite = true)
+        } else {
+            throw IllegalStateException("local.properties doesn't exist at: $localProperties")
+        }
+    }
+
+    private fun writeGradleProperties() {
+        gradlePropertiesFile.writer().use {
+            val props = Properties()
+            props.setProperty("android.useAndroidX", "true")
+            props.store(it, null)
+        }
+    }
+}
+
+data class ProjectProps(
+    val prebuiltsRoot: String,
+    val compileSdkVersion: String,
+    val buildToolsVersion: String,
+    val minSdkVersion: String,
+    val debugKeystore: String,
+    var navigationCommon: String,
+    val kotlinStblib: String,
+    val rootProjectPath: String,
+    val localSupportRepo: String,
+    val agpDependency: String
+) {
+    companion object {
+        fun load(): ProjectProps {
+            val stream = ProjectSetupRule::class.java.classLoader.getResourceAsStream("sdk.prop")
+            val properties = Properties()
+            properties.load(stream)
+            return ProjectProps(
+                prebuiltsRoot = properties.getProperty("prebuiltsRoot"),
+                compileSdkVersion = properties.getProperty("compileSdkVersion"),
+                buildToolsVersion = properties.getProperty("buildToolsVersion"),
+                minSdkVersion = properties.getProperty("minSdkVersion"),
+                debugKeystore = properties.getProperty("debugKeystore"),
+                navigationCommon = properties.getProperty("navigationCommon"),
+                kotlinStblib = properties.getProperty("kotlinStdlib"),
+                rootProjectPath = properties.getProperty("rootProjectPath"),
+                localSupportRepo = properties.getProperty("localSupportRepo"),
+                agpDependency = properties.getProperty("agpDependency")
+            )
+        }
+    }
+}