Add Kotlin support for testFixtures and screenshotTest in AGP
This change adds support for kotlin compilation in testFixtures with the
android.experimental.enableTestFixturesKotlinSupport Gradle property.
Similarly, kotlin compilation is enabled for screenshotTest with the
existing android.experimental.enableScreenshotTest Gradle property.
Bug: 259523353
Test: TestFixturesKotlinTest, ComposeHelloWorldTest, etc.
Change-Id: I35d20ba04d323529b0f35ad81ee8ed2ee58dd083
diff --git a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ComponentImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ComponentImpl.kt
index 9a18b8e..2232ca0 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ComponentImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ComponentImpl.kt
@@ -178,6 +178,8 @@
ProductFlavor(it.first, it.second)
}
+ override val useBuiltInKotlinSupport: Boolean = false
+
// ---------------------------------------------------------------------------------------------
// Private stuff
// ---------------------------------------------------------------------------------------------
diff --git a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/HostTestImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/HostTestImpl.kt
index efaf194..3a904c2 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/HostTestImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/HostTestImpl.kt
@@ -59,6 +59,7 @@
global: GlobalTaskCreationConfig,
hostTestBuilder: HostTestBuilderImpl,
override val hostTestName: String,
+ override val useBuiltInKotlinSupport: Boolean,
) : TestComponentImpl<HostTestComponentDslInfo>(
componentIdentity,
buildFeatureValues,
diff --git a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/KmpComponentImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/KmpComponentImpl.kt
index e1c26b7..8e7dfe8 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/KmpComponentImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/KmpComponentImpl.kt
@@ -68,7 +68,6 @@
import com.android.builder.core.ComponentType
import com.android.utils.appendCapitalized
import org.gradle.api.artifacts.Configuration
-import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.util.PatternSet
@@ -136,6 +135,8 @@
override val minSdk: AndroidVersion
get() = dslInfo.minSdkVersion
+ final override val useBuiltInKotlinSupport = false
+
override val sources = KmpSourcesImpl(
dslInfo,
internalServices,
diff --git a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ScreenshotTestImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ScreenshotTestImpl.kt
index 54c804a..20de4fb 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ScreenshotTestImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/ScreenshotTestImpl.kt
@@ -68,4 +68,5 @@
global,
hostTestBuilder,
HostTestBuilder.SCREENSHOT_TEST_TYPE,
+ useBuiltInKotlinSupport = true
), HostTestCreationConfig
diff --git a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/TestFixturesImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/TestFixturesImpl.kt
index 791b41b..6bc51aa 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/TestFixturesImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/TestFixturesImpl.kt
@@ -43,7 +43,9 @@
import com.android.build.gradle.internal.tasks.AarMetadataTask.Companion.DEFAULT_MIN_COMPILE_SDK_EXTENSION
import com.android.build.gradle.internal.tasks.factory.GlobalTaskCreationConfig
import com.android.build.gradle.internal.testFixtures.testFixturesFeatureName
+import com.android.build.gradle.internal.utils.isKotlinBaseApiPluginApplied
import com.android.build.gradle.internal.variant.VariantPathHelper
+import com.android.build.gradle.options.BooleanOption
import com.android.builder.core.BuilderConstants
import com.android.utils.appendCapitalized
import com.android.utils.capitalizeAndAppend
@@ -167,4 +169,8 @@
override fun getArtifactName(name: String): String {
return "$testFixturesFeatureName-$name"
}
+
+ override val useBuiltInKotlinSupport: Boolean =
+ internalServices.projectOptions.get(BooleanOption.ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT)
+ && isKotlinBaseApiPluginApplied(internalServices.projectInfo)
}
diff --git a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/UnitTestImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/UnitTestImpl.kt
index 70e5e82..3e83179 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/UnitTestImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/api/component/impl/UnitTestImpl.kt
@@ -70,6 +70,7 @@
global,
hostTestBuilder,
HostTestBuilder.UNIT_TEST_TYPE,
+ useBuiltInKotlinSupport = false
), UnitTest {
/**
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/TaskManager.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/TaskManager.kt
index 9685cd8..27cf91b 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/TaskManager.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/TaskManager.kt
@@ -131,6 +131,7 @@
import com.android.build.gradle.internal.testing.utp.TEST_RESULT_PB_FILE_NAME
import com.android.build.gradle.internal.transforms.ShrinkAppBundleResourcesTask
import com.android.build.gradle.internal.transforms.ShrinkResourcesNewShrinkerTask
+import com.android.build.gradle.internal.utils.checkKotlinStdLibIsInDependencies
import com.android.build.gradle.internal.utils.getProjectKotlinPluginKotlinVersion
import com.android.build.gradle.internal.utils.isKotlinKaptPluginApplied
import com.android.build.gradle.internal.utils.isKspPluginApplied
@@ -144,6 +145,7 @@
import com.android.build.gradle.tasks.GenerateResValues
import com.android.build.gradle.tasks.JavaCompileCreationAction
import com.android.build.gradle.tasks.JavaPreCompileTask
+import com.android.build.gradle.tasks.KotlinCompileCreationAction
import com.android.build.gradle.tasks.ManifestProcessorTask
import com.android.build.gradle.tasks.MapSourceSetPathsTask
import com.android.build.gradle.tasks.MergeResources
@@ -856,7 +858,18 @@
)
}
- creationConfig
+ if (creationConfig.useBuiltInKotlinSupport) {
+ creationConfig
+ .artifacts
+ .forScope(ScopedArtifacts.Scope.PROJECT)
+ .setInitialContent(
+ ScopedArtifact.CLASSES,
+ creationConfig.artifacts,
+ InternalArtifactType.KOTLINC
+ )
+ }
+
+ creationConfig
.artifacts
.forScope(ScopedArtifacts.Scope.PROJECT)
.setInitialContent(
@@ -874,6 +887,8 @@
protected fun createJavacTask(
creationConfig: ComponentCreationConfig
): TaskProvider<out JavaCompile> {
+ maybeCreateKotlinTasks(creationConfig)
+
val usingKapt = isKotlinKaptPluginApplied(project)
val usingKsp = isKspPluginApplied(project)
taskFactory.register(JavaPreCompileTask.CreationAction(creationConfig, usingKapt, usingKsp))
@@ -889,6 +904,14 @@
return javacTask
}
+ private fun maybeCreateKotlinTasks(creationConfig: ComponentCreationConfig) {
+ if (!creationConfig.useBuiltInKotlinSupport) {
+ return
+ }
+ checkKotlinStdLibIsInDependencies(project, creationConfig)
+ KotlinCompileCreationAction(creationConfig).registerTask()
+ }
+
/**
* Creates the individual managed device tasks for the given variant
*
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/component/ComponentCreationConfig.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/component/ComponentCreationConfig.kt
index 62cb5ad..822b4d6 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/component/ComponentCreationConfig.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/component/ComponentCreationConfig.kt
@@ -74,6 +74,8 @@
val debuggable: Boolean
val minSdk: AndroidVersion
+ val useBuiltInKotlinSupport: Boolean
+
// ---------------------------------------------------------------------------------------------
// OPTIONAL FEATURES
// ---------------------------------------------------------------------------------------------
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/feature/BundleAllClasses.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/feature/BundleAllClasses.kt
index 7771136..8bc6b5a 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/feature/BundleAllClasses.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/feature/BundleAllClasses.kt
@@ -18,7 +18,6 @@
import com.android.SdkConstants.FN_CLASSES_JAR
import com.android.build.api.artifact.ScopedArtifact
-import com.android.build.api.instrumentation.FramesComputationMode
import com.android.build.api.variant.ScopedArtifacts
import com.android.build.gradle.internal.component.ComponentCreationConfig
import com.android.build.gradle.internal.profile.ProfileAwareWorkAction
@@ -181,6 +180,9 @@
} else {
task.inputDirs.from(
listOfNotNull(
+ creationConfig.artifacts
+ .get(InternalArtifactType.KOTLINC)
+ .takeIf { creationConfig.useBuiltInKotlinSupport },
creationConfig.artifacts.get(InternalArtifactType.JAVAC),
creationConfig.oldVariantApiLegacySupport?.variantData?.allPreJavacGeneratedBytecode,
creationConfig.oldVariantApiLegacySupport?.variantData?.allPostJavacGeneratedBytecode
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/ide/v2/ModelBuilder.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/ide/v2/ModelBuilder.kt
index 52583b5..1688c62 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/ide/v2/ModelBuilder.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/ide/v2/ModelBuilder.kt
@@ -789,6 +789,11 @@
component.androidResourcesCreationConfig?.compiledRClassArtifact?.get()?.asFile?.let {
classesFolders.add(it)
}
+ if (component.useBuiltInKotlinSupport) {
+ classesFolders.add(
+ component.artifacts.get(InternalArtifactType.KOTLINC).get().asFile
+ )
+ }
val generatedClassPaths = addGeneratedClassPaths(component, classesFolders)
@@ -930,10 +935,13 @@
// steps that create bytecode
val classesFolders = mutableSetOf<File>()
classesFolders.add(component.artifacts.get(InternalArtifactType.JAVAC).get().asFile)
- component.oldVariantApiLegacySupport?.let{
+ component.oldVariantApiLegacySupport?.let {
classesFolders.addAll(it.variantData.allPreJavacGeneratedBytecode.files)
classesFolders.addAll(it.variantData.allPostJavacGeneratedBytecode.files)
}
+ if (component.useBuiltInKotlinSupport) {
+ classesFolders.add(component.artifacts.get(InternalArtifactType.KOTLINC).get().asFile)
+ }
// The separately compile R class, if applicable.
if (extension.testOptions.unitTests.isIncludeAndroidResources ||
component.componentType.isForScreenshotPreview) {
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/lint/AndroidLintInputs.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/lint/AndroidLintInputs.kt
index ef1aa89..f32f1ee 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/lint/AndroidLintInputs.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/lint/AndroidLintInputs.kt
@@ -1787,6 +1787,9 @@
)
} else {
classesOutputDirectories.from(creationConfig.artifacts.get(InternalArtifactType.JAVAC))
+ if (creationConfig.useBuiltInKotlinSupport) {
+ classesOutputDirectories.from(creationConfig.artifacts.get(InternalArtifactType.KOTLINC))
+ }
}
creationConfig.oldVariantApiLegacySupport?.variantData?.let {
classesOutputDirectories.from(
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/plugins/AndroidPluginBaseServices.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/plugins/AndroidPluginBaseServices.kt
index b204681..a982681 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/plugins/AndroidPluginBaseServices.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/plugins/AndroidPluginBaseServices.kt
@@ -41,6 +41,7 @@
import com.android.build.gradle.internal.services.AndroidLocationsBuildService
import com.android.build.gradle.internal.services.DslServices
import com.android.build.gradle.internal.services.ProjectServices
+import com.android.build.gradle.internal.utils.MINIMUM_INTEGRATED_KOTLIN_VERSION
import com.android.build.gradle.internal.utils.MUTUALLY_EXCLUSIVE_ANDROID_GRADLE_PLUGINS
import com.android.build.gradle.options.BooleanOption
import com.android.build.gradle.options.ProjectOptionService
@@ -56,6 +57,7 @@
import org.gradle.api.configuration.BuildFeatures
import org.gradle.api.tasks.StopExecutionException
import org.gradle.build.event.BuildEventsListenerRegistry
+import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin
import java.util.Locale
abstract class AndroidPluginBaseServices(
@@ -138,6 +140,30 @@
NoOpAnalyticsService.RegistrationAction(project).execute()
}
+ if (projectOptions.get(BooleanOption.ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT)
+ || projectOptions.get(BooleanOption.ENABLE_SCREENSHOT_TEST)) {
+ try {
+ project.plugins.apply(KotlinBaseApiPlugin::class.java)
+ } catch (e: Throwable) {
+ if (e is ClassNotFoundException || e is NoClassDefFoundError) {
+ val message =
+ """
+ The Kotlin Gradle plugin was not found on the project's buildscript
+ classpath. Add "org.jetbrains.kotlin:kotlin-gradle-plugin:$MINIMUM_INTEGRATED_KOTLIN_VERSION" to the
+ buildscript classpath in order to use any of the following Gradle
+ properties:
+
+ ${BooleanOption.ENABLE_SCREENSHOT_TEST.propertyName},
+ ${BooleanOption.ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT.propertyName}
+
+ """.trimIndent()
+ syncIssueReporter.reportError(Type.GENERIC, message)
+ } else {
+ throw e
+ }
+ }
+ }
+
SyncIssueReporterImpl.GlobalSyncIssueService.RegistrationAction(
project,
SyncOptions.getModelQueryMode(projectOptions),
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/InternalArtifactType.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/InternalArtifactType.kt
index 65856ea..c682eda 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/InternalArtifactType.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/InternalArtifactType.kt
@@ -49,6 +49,8 @@
// module: InternalArtifactType<RegularFile>(FILE), Replaceable use AnchorOutputType.ALL_CLASSES
// Javac task output.
object JAVAC: InternalArtifactType<Directory>(DIRECTORY), Replaceable
+ // Kotlin Built-in Support compile task output
+ object KOTLINC: InternalArtifactType<Directory>(DIRECTORY), Replaceable
// --- Published classes ---
// Class-type task output for tasks that generate published classes.
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/ProjectInfo.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/ProjectInfo.kt
index 125aa70..50d1fc6 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/ProjectInfo.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/scope/ProjectInfo.kt
@@ -18,6 +18,7 @@
import com.android.SdkConstants
import com.android.builder.core.BuilderConstants
+import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.capabilities.Capability
import org.gradle.api.file.Directory
@@ -99,6 +100,12 @@
fun hasPlugin(plugin: String): Boolean = project.plugins.hasPlugin(plugin)
+ fun <T : Plugin<*>> hasPlugin(pluginClass: Class<T>): Boolean =
+ project.plugins.hasPlugin(pluginClass)
+
+ fun <T : Plugin<*>> findPlugin(pluginClass: Class<T>): T? =
+ project.plugins.findPlugin(pluginClass)
+
@Deprecated("Use buildDirectory instead")
fun getBuildDir(): File {
return project.buildDir
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServices.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServices.kt
index 9bb6ead..e26f402 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServices.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServices.kt
@@ -19,9 +19,15 @@
import com.android.build.gradle.internal.errors.DeprecationReporter
import com.android.build.gradle.internal.scope.ProjectInfo
import com.android.build.gradle.internal.utils.GradleEnvironmentProvider
+import com.android.build.gradle.internal.utils.MINIMUM_INTEGRATED_KOTLIN_VERSION
+import com.android.build.gradle.internal.utils.getKotlinPluginVersionFromPlugin
+import com.android.build.gradle.options.BooleanOption
import com.android.build.gradle.options.ProjectOptions
import com.android.builder.errors.IssueReporter
+import com.android.ide.common.gradle.Version
import org.gradle.api.services.BuildServiceRegistry
+import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin
+import org.jetbrains.kotlin.gradle.plugin.KotlinJvmFactory
import java.io.File
/**
@@ -35,8 +41,44 @@
val buildServiceRegistry: BuildServiceRegistry
val gradleEnvironmentProvider: GradleEnvironmentProvider
val projectInfo: ProjectInfo
+ val kotlinServices: KotlinServices?
fun <T> newInstance(type: Class<T>, vararg args: Any?): T
fun file(file: Any): File
}
+
+interface KotlinServices {
+
+ val kgpVersion: String
+ val factory: KotlinJvmFactory
+
+ companion object {
+
+ fun createFromPlugin(plugin: KotlinBaseApiPlugin?): KotlinServices? {
+ plugin ?: return null
+ getKotlinPluginVersionFromPlugin(plugin)?.let {
+ if (Version.parse(it) < Version.parse(MINIMUM_INTEGRATED_KOTLIN_VERSION)) {
+ val message =
+ """
+ The current Kotlin Gradle plugin version ($it) is below the required
+ minimum version ($MINIMUM_INTEGRATED_KOTLIN_VERSION).
+
+ The following Gradle properties require the Kotlin Gradle plugin version
+ to be at least $MINIMUM_INTEGRATED_KOTLIN_VERSION:
+
+ ${BooleanOption.ENABLE_SCREENSHOT_TEST.propertyName},
+ ${BooleanOption.ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT.propertyName}
+
+ """.trimIndent()
+ throw RuntimeException(message)
+ }
+ }
+
+ return object : KotlinServices {
+ override val kgpVersion: String = plugin.pluginVersion
+ override val factory: KotlinJvmFactory = plugin
+ }
+ }
+ }
+}
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServicesImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServicesImpl.kt
index 72ca90c..501d356 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServicesImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/services/BaseServicesImpl.kt
@@ -18,8 +18,10 @@
import com.android.build.gradle.internal.errors.DeprecationReporter
import com.android.build.gradle.internal.scope.ProjectInfo
+import com.android.build.gradle.internal.services.KotlinServices.Companion.createFromPlugin
import com.android.build.gradle.internal.utils.GradleEnvironmentProvider
import com.android.build.gradle.internal.utils.GradleEnvironmentProviderImpl
+import com.android.build.gradle.internal.utils.findKotlinBaseApiPlugin
import com.android.build.gradle.options.ProjectOptions
import com.android.builder.errors.IssueReporter
import org.gradle.api.services.BuildServiceRegistry
@@ -49,4 +51,9 @@
final override val projectInfo: ProjectInfo
get() = projectServices.projectInfo
+
+ override val kotlinServices: KotlinServices? by lazy {
+ val kotlinBaseApiPlugin = findKotlinBaseApiPlugin(projectInfo) ?: return@lazy null
+ createFromPlugin(kotlinBaseApiPlugin)
+ }
}
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfig.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfig.kt
index 1700695..e340f68 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfig.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfig.kt
@@ -45,6 +45,7 @@
import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
/**
* Creation config for global tasks that are not variant-based.
@@ -93,6 +94,8 @@
val unitTestOptions: UnitTestOptionsDslInfo
val testServers: List<TestServer>
+ val kotlinOptions: KotlinJvmOptions?
+
// processed access to some DSL values
val namespacedAndroidResources: Boolean
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfigImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfigImpl.kt
index e14e453..000fb6f 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfigImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/GlobalTaskCreationConfigImpl.kt
@@ -64,7 +64,9 @@
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection
+import org.gradle.api.plugins.ExtensionAware
import org.gradle.api.provider.Provider
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
class GlobalTaskCreationConfigImpl(
project: Project,
@@ -188,6 +190,9 @@
override val testServers: List<TestServer>
get() = oldExtension.testServers
+ override val kotlinOptions: KotlinJvmOptions?
+ get() = (oldExtension as ExtensionAware).extensions.findByName("kotlinOptions") as? KotlinJvmOptions
+
override val namespacedAndroidResources: Boolean
get() = extension.androidResources.namespaced
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/KmpGlobalTaskCreationConfigImpl.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/KmpGlobalTaskCreationConfigImpl.kt
index 3fe6715..20b388b 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/KmpGlobalTaskCreationConfigImpl.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/tasks/factory/KmpGlobalTaskCreationConfigImpl.kt
@@ -64,6 +64,7 @@
import org.gradle.api.file.Directory
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
internal class KmpGlobalTaskCreationConfigImpl(
project: Project,
@@ -253,4 +254,6 @@
get() = throw IllegalAccessException("Not supported for kmp")
override val dataBinding: DataBinding
get() = throw IllegalAccessException("Not supported for kmp")
+ override val kotlinOptions: KotlinJvmOptions
+ get() = throw IllegalAccessException("Not supported for kmp")
}
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/utils/kgpUtils.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/utils/kgpUtils.kt
index 0a31a41..6ac4058 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/utils/kgpUtils.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/internal/utils/kgpUtils.kt
@@ -24,17 +24,22 @@
import com.android.build.gradle.internal.component.ComponentCreationConfig
import com.android.build.gradle.internal.component.LibraryCreationConfig
import com.android.build.gradle.internal.profile.AnalyticsConfiguratorService
+import com.android.build.gradle.internal.scope.ProjectInfo
import com.android.build.gradle.internal.services.getBuildService
+import com.android.builder.errors.IssueReporter
import com.android.utils.appendCapitalized
import com.google.wireless.android.sdk.stats.GradleBuildVariant
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.component.ModuleComponentIdentifier
+import org.gradle.api.artifacts.result.ResolvedDependencyResult
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.tasks.ClasspathNormalizer
import org.gradle.api.tasks.SourceSet
+import org.jetbrains.kotlin.gradle.plugin.KotlinBaseApiPlugin
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
@@ -160,20 +165,19 @@
/** Records information from KGP for analytics. */
fun recordKgpPropertiesForAnalytics(project: Project, creationConfigs: List<ComponentCreationConfig>) {
configureKotlinCompileTasks(project, creationConfigs) { kotlinCompile, creationConfig ->
- recordKotlinCompilePropertiesForAnalytics(kotlinCompile, creationConfig, project)
+ recordKotlinCompilePropertiesForAnalytics(kotlinCompile, creationConfig)
}
}
-private fun recordKotlinCompilePropertiesForAnalytics(
+fun recordKotlinCompilePropertiesForAnalytics(
kotlinCompile: KotlinCompile,
- creationConfig: ComponentCreationConfig,
- project: Project
+ creationConfig: ComponentCreationConfig
) {
getLanguageVersionUnsafe(kotlinCompile)?.let { languageVersion ->
getBuildService(
creationConfig.services.buildServiceRegistry,
AnalyticsConfiguratorService::class.java
- ).get().getVariantBuilder(project.path, creationConfig.name)?.apply {
+ ).get().getVariantBuilder(creationConfig.services.projectInfo.path, creationConfig.name)?.apply {
setKotlinOptions(
GradleBuildVariant.KotlinOptions.newBuilder().setLanguageVersion(languageVersion)
)
@@ -322,3 +326,61 @@
creationConfig.services.configurations.findByName(configurationName)
}
}
+
+fun isKotlinBaseApiPluginApplied(projectInfo: ProjectInfo): Boolean =
+ try {
+ projectInfo.hasPlugin(KotlinBaseApiPlugin::class.java)
+ } catch (e: Throwable) {
+ if (e is ClassNotFoundException || e is NoClassDefFoundError) false else throw e
+ }
+
+fun findKotlinBaseApiPlugin(projectInfo: ProjectInfo): KotlinBaseApiPlugin? =
+ try {
+ projectInfo.findPlugin(KotlinBaseApiPlugin::class.java)
+ } catch (e: Throwable) {
+ if (e is ClassNotFoundException || e is NoClassDefFoundError) null else throw e
+ }
+
+/**
+ * Check that the kotlin stdlib is present in the compile and runtime classpaths
+ *
+ * KGP automatically adds the kotlin stdlib dependency, which is not desirable behavior. Instead of
+ * doing that, AGP checks for the dependency when the "com.android.kotlin" plugin is applied and
+ * instructs users to add the dependency if it is missing.
+ */
+internal fun checkKotlinStdLibIsInDependencies(
+ project: Project,
+ creationConfig: ComponentCreationConfig
+) {
+ val defaultVersion = creationConfig.services.kotlinServices?.kgpVersion ?: return
+
+ // TODO(b/259523353): add DSL flag to ignore this check
+ fun Configuration.checkKotlinStdlibPresent() {
+ incoming.afterResolve {
+ val hasKotlinStdLib = it.resolutionResult.allDependencies
+ .filterIsInstance<ResolvedDependencyResult>()
+ .map { it.selected.id }
+ .filterIsInstance<ModuleComponentIdentifier>()
+ .any {
+ it.group == KOTLIN_GROUP && (it.module == "kotlin-stdlib" || it.module == "kotlin-stdlib-jdk8")
+ }
+ if (!hasKotlinStdLib) {
+ creationConfig.services.issueReporter.reportError(
+ IssueReporter.Type.GENERIC,
+ """
+Kotlin standard library is missing from ${this.name}. Please add a dependency on
+"$KOTLIN_GROUP:kotlin-stdlib:$defaultVersion" to your build file: `${project.buildFile.toURI()}`
+ """.trimIndent()
+ )
+ }
+ }
+ }
+
+ creationConfig.variantDependencies.compileClasspath.checkKotlinStdlibPresent()
+ creationConfig.variantDependencies.runtimeClasspath.checkKotlinStdlibPresent()
+}
+
+private const val KOTLIN_GROUP = "org.jetbrains.kotlin"
+// The minimum version of KGP required to be on the buildscript classpath for integrated kotlin
+// support in AGP
+const val MINIMUM_INTEGRATED_KOTLIN_VERSION = "1.9.20"
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/options/BooleanOption.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/options/BooleanOption.kt
index c446e80..f6c7600 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/options/BooleanOption.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/options/BooleanOption.kt
@@ -306,6 +306,15 @@
FeatureStage.Experimental
),
+ /**
+ * Whether to enable kotlin compilation for test fixtures
+ */
+ ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT(
+ "android.experimental.enableTestFixturesKotlinSupport",
+ false,
+ FeatureStage.Experimental
+ ),
+
/* ------------------------
* SOFTLY-ENFORCED FEATURES
*/
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/JavaCompileUtils.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/JavaCompileUtils.kt
index 4c38c96..f5ec8b5 100644
--- a/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/JavaCompileUtils.kt
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/JavaCompileUtils.kt
@@ -33,6 +33,7 @@
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.JAR
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ConsumedConfigType.ANNOTATION_PROCESSOR
import com.android.build.gradle.internal.services.getBuildService
+import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.options.BooleanOption
import com.android.builder.errors.DefaultIssueReporter
import com.android.builder.errors.IssueReporter
@@ -98,11 +99,19 @@
// classes(e.g. android.jar) that were previously passed through bootstrapClasspath need to be provided
// through classpath
creationConfig.global.bootClasspath,
- creationConfig.compileClasspath
+ creationConfig.compileClasspath,
+ creationConfig.artifacts
+ .get(InternalArtifactType.KOTLINC)
+ .takeIf { creationConfig.useBuiltInKotlinSupport },
)
} else {
this.options.bootstrapClasspath = this.project.files(creationConfig.global.bootClasspath)
- this.classpath = creationConfig.compileClasspath
+ this.classpath = project.files(
+ creationConfig.compileClasspath,
+ creationConfig.artifacts
+ .get(InternalArtifactType.KOTLINC)
+ .takeIf { creationConfig.useBuiltInKotlinSupport },
+ )
}
this.sourceCompatibility = compileOptions.sourceCompatibility.toString()
diff --git a/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/KotlinCompileCreationAction.kt b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/KotlinCompileCreationAction.kt
new file mode 100644
index 0000000..e6561a5
--- /dev/null
+++ b/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/KotlinCompileCreationAction.kt
@@ -0,0 +1,131 @@
+/*
+ * 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 com.android.build.gradle.tasks
+
+import com.android.build.gradle.internal.component.ComponentCreationConfig
+import com.android.build.gradle.internal.component.NestedComponentCreationConfig
+import com.android.build.gradle.internal.profile.PROPERTY_VARIANT_NAME_KEY
+import com.android.build.gradle.internal.publishing.AndroidArtifacts
+import com.android.build.gradle.internal.publishing.PublishingSpecs
+import com.android.build.gradle.internal.scope.InternalArtifactType
+import com.android.build.gradle.internal.scope.MutableTaskContainer
+import org.gradle.api.tasks.TaskProvider
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions
+import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
+
+class KotlinCompileCreationAction(private val creationConfig: ComponentCreationConfig) {
+
+ val taskName: String = creationConfig.computeTaskNameInternal("compile", "Kotlin")
+
+ private fun getTaskFactory(): TaskProvider<out KotlinJvmCompile> {
+ return creationConfig.services
+ .kotlinServices!!
+ .factory
+ .registerKotlinJvmCompileTask(
+ taskName,
+ creationConfig.name
+ )
+ }
+
+ private fun handleProvider(task: TaskProvider<out KotlinJvmCompile>) {
+ val artifacts = creationConfig.artifacts
+ artifacts.setInitialProvider(task) { it.destinationDirectory }
+ .withName("classes")
+ .on(InternalArtifactType.KOTLINC)
+ }
+
+ private fun configureTask(task: KotlinJvmCompile) {
+ creationConfig.sources.kotlin {
+ task.source(it.getAsFileTrees())
+ }
+ creationConfig.sources.java {
+ task.source(it.getAsFileTrees())
+ }
+
+ val taskClasspath =
+ creationConfig.services.fileCollection().from(
+ creationConfig.getJavaClasspath(
+ AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH,
+ AndroidArtifacts.ArtifactType.CLASSES_JAR,
+ null
+ ),
+ creationConfig.global.bootClasspath
+ )
+ task.libraries.setFrom(taskClasspath)
+
+ task.sourceSetName.set(creationConfig.name)
+ task.useModuleDetection.set(true)
+ task.multiPlatformEnabled.set(false)
+
+ task.pluginClasspath
+ .from(creationConfig.services.kotlinServices!!.factory.getCompilerPlugins())
+ // TODO(b/259523353) - fix this
+ // task.pluginOptions.addAll(creationConfig.kotlinCompilerOptions!!)
+
+ // Add friendPaths to allow access to internal properties of main variant
+ if (creationConfig is NestedComponentCreationConfig) {
+ val mainVariant = creationConfig.mainVariant
+ val internalArtifactType =
+ PublishingSpecs.getVariantPublishingSpec(mainVariant.componentType)
+ .getSpec(
+ AndroidArtifacts.ArtifactType.CLASSES_JAR,
+ AndroidArtifacts.ConsumedConfigType.COMPILE_CLASSPATH.publishedTo
+ )
+ ?.outputType
+ internalArtifactType?.let {
+ task.friendPaths.from(
+ creationConfig.services.fileCollection(mainVariant.artifacts.get(it))
+ )
+ }
+ }
+
+ creationConfig.global.kotlinOptions?.let { task.applyJvmOptions(it) }
+ }
+
+ fun registerTask() {
+ val taskProvider = getTaskFactory()
+ handleProvider(taskProvider)
+
+ taskProvider.configure {
+ val taskContainer: MutableTaskContainer = creationConfig.taskContainer
+ it.dependsOn(taskContainer.preBuildTask)
+ it.extensions.add(PROPERTY_VARIANT_NAME_KEY, creationConfig.name)
+
+ configureTask(it)
+ }
+ }
+}
+
+private fun KotlinJvmCompile.applyJvmOptions(options: KotlinJvmOptions) {
+ kotlinOptions {
+ // TODO(b/259523353): Initialize task options from the DSL object, add API in KGP for
+ // automatic copying
+ jvmTarget = options.jvmTarget
+ javaParameters = options.javaParameters
+ moduleName = options.moduleName
+ noJdk = options.noJdk
+
+ apiVersion = options.apiVersion
+ languageVersion = options.languageVersion
+ useK2 = options.useK2
+
+ freeCompilerArgs = options.freeCompilerArgs
+ allWarningsAsErrors = options.allWarningsAsErrors
+ suppressWarnings = options.suppressWarnings
+ verbose = options.verbose
+ }
+}
diff --git a/build-system/integration-test/application/BUILD.bazel b/build-system/integration-test/application/BUILD.bazel
index d558d8e..90c7482 100644
--- a/build-system/integration-test/application/BUILD.bazel
+++ b/build-system/integration-test/application/BUILD.bazel
@@ -1,5 +1,5 @@
load("//tools/base/bazel:maven.bzl", "maven_repository")
-load("//tools/base/build-system/integration-test:common-dependencies.bzl", "KGP_VERSION_FOR_TESTS")
+load("//tools/base/build-system/integration-test:common-dependencies.bzl", "KGP_VERSION_FOR_TESTS", "KGP_1_9_22", "KGP_1_8_10")
load("//tools/base/build-system/integration-test:integration-test.bzl", "gradle_integration_test", "single_gradle_integration_test_per_source")
load("//tools/base/build-system/integration-test:integration-test.bzl", "single_gradle_integration_test")
@@ -159,6 +159,7 @@
"//tools/base/build-system/integration-test:test-projects/sourceDependency",
"//tools/base/build-system/integration-test:test-projects/testDependency",
"//tools/base/build-system/integration-test:test-projects/testFixturesApp",
+ "//tools/base/build-system/integration-test:test-projects/testFixturesKotlinApp",
"//tools/base/build-system/integration-test:test-projects/testWithDep",
"//tools/base/build-system/integration-test:test-projects/tictactoe",
"//tools/base/build-system/integration-test:test-projects/transformApiTest",
@@ -249,6 +250,7 @@
exclude = SOURCES_TO_COMPILE_TOGETHER + CONNECTED_TESTS + CHECK_ALL_SOURCES + [
# These subpackages have their own target definitions.
"src/test/java/com/android/build/gradle/integration/packaging/**",
+ "src/test/java/com/android/build/gradle/integration/kotlin/**",
"src/test/java/com/android/build/gradle/integration/library/**",
"src/test/java/com/android/build/gradle/integration/mlkit/**",
"src/test/java/com/android/build/gradle/integration/testing/**",
@@ -417,6 +419,44 @@
deps = TEST_DEPS,
)
+maven_repository(
+ name = "kotlin_1_8_10",
+ artifacts = KGP_1_8_10,
+ visibility = [":__subpackages__"],
+)
+
+maven_repository(
+ name = "kotlin_1_9_22",
+ artifacts = KGP_1_9_22,
+ visibility = [":__subpackages__"],
+)
+
+gradle_integration_test(
+ name = "built-in-kotlin-support-tests",
+ srcs = glob(
+ [
+ "src/test/java/com/android/build/gradle/integration/kotlin/*.java",
+ "src/test/java/com/android/build/gradle/integration/kotlin/*.kt",
+ ],
+ ),
+ data = TEST_DATA,
+ maven_repo_zips = [
+ "//tools/base/build-system:android_gradle_plugin",
+ ],
+ maven_repos = [
+ "//tools/base/build-system:android_gradle_plugin_runtime_dependencies",
+ "//tools/base/build-system/integration-test:androidx_latest",
+ "//tools/base/build-system/integration-test:support_library_latest",
+ "//tools/base/build-system/integration-test:kotlin_gradle_plugin_prebuilts",
+ "//tools/base/build-system/integration-test/application:kotlin_1_8_10",
+ "//tools/base/build-system/integration-test/application:kotlin_1_9_22",
+ "//tools/base/build-system/integration-test/application:prebuilts",
+ ],
+ resources = glob(["src/test/resources/**"]),
+ shard_count = 2,
+ deps = TEST_DEPS,
+)
+
gradle_integration_test(
name = "dependencies-tests",
timeout = "long",
diff --git a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/application/ComposeHelloWorldTest.kt b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/application/ComposeHelloWorldTest.kt
index 144401f..b3ec034 100644
--- a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/application/ComposeHelloWorldTest.kt
+++ b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/application/ComposeHelloWorldTest.kt
@@ -18,6 +18,7 @@
import com.android.build.gradle.integration.common.fixture.GradleTestProject
import com.android.build.gradle.integration.common.utils.TestFileUtils
+import com.android.testutils.truth.PathSubject.assertThat
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -54,4 +55,37 @@
val result = project.executor().run("assembleDebug")
assertThat(result.didWorkTasks).contains(":app:compileDebugKotlin")
}
+
+ @Test
+ fun testScreenshotTestAndTestFixturesCompilation() {
+ project.executor().run(":app:compileDebugTestFixturesKotlin")
+ val testFixturesClassFile =
+ project.getSubproject("app")
+ .getIntermediateFile(
+ "kotlinc",
+ "debugTestFixtures",
+ "compileDebugTestFixturesKotlin",
+ "classes",
+ "com",
+ "example",
+ "helloworldcompose",
+ "FixtureKt.class"
+ )
+ assertThat(testFixturesClassFile).exists()
+
+ project.executor().run(":app:compileDebugScreenshotTestKotlin")
+ val screenshotTestClassFile =
+ project.getSubproject("app")
+ .getIntermediateFile(
+ "kotlinc",
+ "debugScreenshotTest",
+ "compileDebugScreenshotTestKotlin",
+ "classes",
+ "com",
+ "example",
+ "helloworldcompose",
+ "ScreenshotTestKt.class"
+ )
+ assertThat(screenshotTestClassFile).exists()
+ }
}
diff --git a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/BuiltInKotlinSupportTest.kt b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/BuiltInKotlinSupportTest.kt
new file mode 100644
index 0000000..dfaab79
--- /dev/null
+++ b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/BuiltInKotlinSupportTest.kt
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2024 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 com.android.build.gradle.integration.kotlin
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject.Companion.VERSION_CATALOG
+import com.android.build.gradle.integration.common.fixture.testprojects.PluginType
+import com.android.build.gradle.integration.common.fixture.testprojects.createGradleProjectBuilder
+import com.android.build.gradle.integration.common.fixture.testprojects.prebuilts.setUpHelloWorld
+import com.android.build.gradle.integration.common.truth.TruthHelper.assertThat
+import com.android.build.gradle.integration.common.utils.TestFileUtils
+import com.android.build.gradle.internal.dsl.ModulePropertyKey.BooleanWithDefault.SCREENSHOT_TEST
+import com.android.build.gradle.options.BooleanOption
+import com.android.testutils.TestUtils
+import com.android.testutils.apk.Aar
+import com.android.testutils.truth.PathSubject
+import org.junit.Rule
+import org.junit.Test
+
+class BuiltInKotlinSupportTest {
+
+ @get:Rule
+ val project =
+ createGradleProjectBuilder {
+ subProject(":lib") {
+ plugins.add(PluginType.ANDROID_LIB)
+ plugins.add(PluginType.KOTLIN_ANDROID)
+ android {
+ setUpHelloWorld()
+ }
+ }
+ }.withKotlinGradlePlugin(true)
+ .create()
+
+ @Test
+ fun testTestFixturesKotlinSupport() {
+ TestFileUtils.appendToFile(
+ project.gradlePropertiesFile,
+ "${BooleanOption.ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT.propertyName}=true"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.testFixtures.enable = true
+
+ kotlin {
+ jvmToolchain(17)
+ }
+
+ dependencies {
+ testFixturesImplementation("org.jetbrains.kotlin:kotlin-stdlib:${TestUtils.KOTLIN_VERSION_FOR_TESTS}")
+ }
+ """.trimIndent()
+ )
+ lib.file("src/testFixtures/kotlin/LibTestFixtureFoo.kt").let {
+ it.parentFile.mkdirs()
+ it.writeText(
+ """
+ package com.foo.library
+ class LibTestFixtureFoo
+ """.trimIndent()
+ )
+ }
+ lib.executor().run(":lib:assembleDebugTestFixtures")
+ val aar = lib.outputDir.resolve("aar").listFiles().single()
+ Aar(aar).use {
+ assertThat(it).containsMainClass("Lcom/foo/library/LibTestFixtureFoo;")
+ }
+ }
+
+ @Test
+ fun testScreenshotTest() {
+ TestFileUtils.appendToFile(
+ project.gradlePropertiesFile,
+ "${BooleanOption.ENABLE_SCREENSHOT_TEST.propertyName}=true"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.experimentalProperties["${SCREENSHOT_TEST.key}"] = true
+
+ kotlin {
+ jvmToolchain(17)
+ }
+
+ dependencies {
+ testFixturesImplementation("org.jetbrains.kotlin:kotlin-stdlib:${TestUtils.KOTLIN_VERSION_FOR_TESTS}")
+ }
+ """.trimIndent()
+ )
+ lib.file("src/screenshotTest/kotlin/LibScreenshotTest.kt").let {
+ it.parentFile.mkdirs()
+ it.writeText(
+ """
+ package com.foo.library
+ class LibScreenshotTest
+ """.trimIndent()
+ )
+ }
+ lib.executor().run(":lib:compileDebugScreenshotTestKotlin")
+ val screenshotTestClassFile =
+ project.getSubproject("lib")
+ .getIntermediateFile(
+ "kotlinc",
+ "debugScreenshotTest",
+ "compileDebugScreenshotTestKotlin",
+ "classes",
+ "com",
+ "foo",
+ "library",
+ "LibScreenshotTest.class"
+ )
+ PathSubject.assertThat(screenshotTestClassFile).exists()
+ }
+
+ @Test
+ fun testInternalModifierAccessibleFromTestFixtures() {
+ TestFileUtils.appendToFile(
+ project.gradlePropertiesFile,
+ "${BooleanOption.ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT.propertyName}=true"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.testFixtures.enable = true
+
+ kotlin {
+ jvmToolchain(17)
+ }
+
+ dependencies {
+ testFixturesImplementation("org.jetbrains.kotlin:kotlin-stdlib:${TestUtils.KOTLIN_VERSION_FOR_TESTS}")
+ }
+ """.trimIndent()
+ )
+ lib.file("src/testFixtures/kotlin/LibTestFixtureFoo.kt").let {
+ it.parentFile.mkdirs()
+ it.writeText(
+ """
+ package com.foo.library
+ class LibTestFixtureFoo {
+ init { LibFoo().bar() }
+ }
+ """.trimIndent()
+ )
+ }
+ lib.getMainSrcDir("java").resolve("LibFoo.kt").let {
+ it.parentFile.mkdirs()
+ it.writeText(
+ """
+ package com.foo.library
+ class LibFoo {
+ internal fun bar() {}
+ }
+ """.trimIndent()
+ )
+ }
+ lib.executor().run(":lib:assembleDebugTestFixtures")
+ }
+
+ @Test
+ fun testInternalModifierAccessibleFromScreenshotTest() {
+ TestFileUtils.appendToFile(
+ project.gradlePropertiesFile,
+ "${BooleanOption.ENABLE_SCREENSHOT_TEST.propertyName}=true"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.experimentalProperties["${SCREENSHOT_TEST.key}"] = true
+
+ kotlin {
+ jvmToolchain(17)
+ }
+
+ dependencies {
+ testFixturesImplementation("org.jetbrains.kotlin:kotlin-stdlib:${TestUtils.KOTLIN_VERSION_FOR_TESTS}")
+ }
+ """.trimIndent()
+ )
+ lib.file("src/screenshotTest/kotlin/LibScreenshotTestFoo.kt").let {
+ it.parentFile.mkdirs()
+ it.writeText(
+ """
+ package com.foo.library
+ class LibScreenshotTestFoo {
+ init { LibFoo().bar() }
+ }
+ """.trimIndent()
+ )
+ }
+ lib.getMainSrcDir("java").resolve("LibFoo.kt").let {
+ it.parentFile.mkdirs()
+ it.writeText(
+ """
+ package com.foo.library
+ class LibFoo {
+ internal fun bar() {}
+ }
+ """.trimIndent()
+ )
+ }
+ lib.executor().run(":lib:compileDebugScreenshotTestKotlin")
+ }
+
+ @Test
+ fun testLowKotlinVersionWithTestFixturesKotlinSupport() {
+ TestFileUtils.searchAndReplace(
+ project.projectDir.parentFile.resolve(VERSION_CATALOG),
+ "version('kotlinVersion', '${TestUtils.KOTLIN_VERSION_FOR_TESTS}')",
+ "version('kotlinVersion', '1.8.10')"
+ )
+ TestFileUtils.appendToFile(
+ project.gradlePropertiesFile,
+ "${BooleanOption.ENABLE_TEST_FIXTURES_KOTLIN_SUPPORT.propertyName}=true"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.testFixtures.enable = true
+
+ kotlin {
+ jvmToolchain(17)
+ }
+
+ dependencies {
+ testFixturesImplementation("org.jetbrains.kotlin:kotlin-stdlib:${TestUtils.KOTLIN_VERSION_FOR_TESTS}")
+ }
+ """.trimIndent()
+ )
+ val result = lib.executor().expectFailure().run(":lib:assembleDebugTestFixtures")
+ result.assertErrorContains(
+ "The current Kotlin Gradle plugin version (1.8.10) is below the required"
+ )
+ }
+
+ @Test
+ fun testLowKotlinVersionWithScreenshotTest() {
+ TestFileUtils.searchAndReplace(
+ project.projectDir.parentFile.resolve(VERSION_CATALOG),
+ "version('kotlinVersion', '${TestUtils.KOTLIN_VERSION_FOR_TESTS}')",
+ "version('kotlinVersion', '1.8.10')"
+ )
+ TestFileUtils.appendToFile(
+ project.gradlePropertiesFile,
+ "${BooleanOption.ENABLE_SCREENSHOT_TEST.propertyName}=true"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.experimentalProperties["${SCREENSHOT_TEST.key}"] = true
+
+ kotlin {
+ jvmToolchain(17)
+ }
+
+ dependencies {
+ testFixturesImplementation("org.jetbrains.kotlin:kotlin-stdlib:${TestUtils.KOTLIN_VERSION_FOR_TESTS}")
+ }
+ """.trimIndent()
+ )
+ val result = lib.executor().expectFailure().run(":lib:compileDebugScreenshotTest")
+ result.assertErrorContains(
+ "The current Kotlin Gradle plugin version (1.8.10) is below the required"
+ )
+ }
+
+ @Test
+ fun testLowKotlinVersionWithNoBuiltInKotlinSupport() {
+ TestFileUtils.searchAndReplace(
+ project.projectDir.parentFile.resolve(VERSION_CATALOG),
+ "version('kotlinVersion', '${TestUtils.KOTLIN_VERSION_FOR_TESTS}')",
+ "version('kotlinVersion', '1.8.10')"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.testFixtures.enable = true
+ """.trimIndent()
+ )
+ // We expect no build failure in this case.
+ // Set failOnWarning to false because Gradle warns about deprecated feature(s) used by KGP 1.8.10.
+ lib.executor().withFailOnWarning(false).run(":lib:assembleDebugTestFixtures")
+ }
+}
diff --git a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/BuiltInKotlinSupportWithoutKgpTest.kt b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/BuiltInKotlinSupportWithoutKgpTest.kt
new file mode 100644
index 0000000..fcb52ef
--- /dev/null
+++ b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/BuiltInKotlinSupportWithoutKgpTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 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 com.android.build.gradle.integration.kotlin
+
+import com.android.build.gradle.integration.common.fixture.testprojects.PluginType
+import com.android.build.gradle.integration.common.fixture.testprojects.createGradleProjectBuilder
+import com.android.build.gradle.integration.common.fixture.testprojects.prebuilts.setUpHelloWorld
+import com.android.build.gradle.integration.common.utils.TestFileUtils
+import com.android.build.gradle.internal.dsl.ModulePropertyKey.BooleanWithDefault.SCREENSHOT_TEST
+import com.android.build.gradle.options.BooleanOption
+import org.junit.Rule
+import org.junit.Test
+
+class BuiltInKotlinSupportWithoutKgpTest {
+
+ @get:Rule
+ val project =
+ createGradleProjectBuilder {
+ subProject(":lib") {
+ plugins.add(PluginType.ANDROID_LIB)
+ android {
+ setUpHelloWorld()
+ }
+ }
+ }.create()
+
+ @Test
+ fun testKgpMissingFromClasspath() {
+ TestFileUtils.appendToFile(
+ project.gradlePropertiesFile,
+ "${BooleanOption.ENABLE_SCREENSHOT_TEST.propertyName}=true"
+ )
+ val lib = project.getSubproject(":lib")
+ lib.buildFile.appendText(
+ """
+ android.experimentalProperties["${SCREENSHOT_TEST.key}"] = true
+ """.trimIndent()
+ )
+ val result = lib.executor().expectFailure().run(":lib:assembleDebug")
+ result.assertErrorContains("The Kotlin Gradle plugin was not found")
+ }
+}
diff --git a/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/TestFixturesKotlinTest.kt b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/TestFixturesKotlinTest.kt
new file mode 100644
index 0000000..0463cdf
--- /dev/null
+++ b/build-system/integration-test/application/src/test/java/com/android/build/gradle/integration/kotlin/TestFixturesKotlinTest.kt
@@ -0,0 +1,538 @@
+/*
+ * Copyright (C) 2024 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 com.android.build.gradle.integration.kotlin
+
+import com.android.build.gradle.integration.common.fixture.GradleTestProject
+import com.android.build.gradle.integration.common.fixture.GradleTestProject.Companion.VERSION_CATALOG
+import com.android.build.gradle.integration.common.utils.TestFileUtils
+import com.android.testutils.TestUtils
+import com.android.testutils.apk.Apk
+import com.android.testutils.truth.PathSubject.assertThat
+import com.google.common.truth.Truth
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import java.io.File
+
+/**
+ * Similar to [TestFixturesTest], but using a project with kotlin test fixtures
+ */
+@RunWith(Parameterized::class)
+class TestFixturesKotlinTest(private val kotlinVersion: String) {
+
+ companion object {
+ @JvmStatic
+ @Parameterized.Parameters(name = "kotlinVersion_{0}")
+ fun parameters() = listOf(TestUtils.KOTLIN_VERSION_FOR_TESTS, "1.9.22")
+ }
+
+ @get:Rule
+ val project: GradleTestProject =
+ GradleTestProject.builder().fromTestProject("testFixturesKotlinApp").create()
+
+ @Before
+ fun before() {
+ TestFileUtils.searchAndReplace(
+ project.projectDir.parentFile.resolve(VERSION_CATALOG),
+ "version('kotlinVersion', '${TestUtils.KOTLIN_VERSION_FOR_TESTS}')",
+ "version('kotlinVersion', '$kotlinVersion' )"
+ )
+ }
+
+ private fun setUpProject(publishJavaLib: Boolean, publishAndroidLib: Boolean) {
+ if (publishJavaLib) {
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":app").buildFile,
+ "project(\":javaLib\")",
+ "'com.example.javaLib:javaLib:1.0'"
+ )
+
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":appTests").buildFile,
+ "project(\":javaLib\")",
+ "'com.example.javaLib:javaLib:1.0'"
+ )
+ }
+
+
+ if (publishAndroidLib) {
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":app").buildFile,
+ "project(\":lib\")",
+ "'com.example.lib:lib:1.0'"
+ )
+
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":lib2").buildFile,
+ "project(\":lib\")",
+ "'com.example.lib:lib:1.0'"
+ )
+
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":appTests").buildFile,
+ "project(\":lib\")",
+ "'com.example.lib:lib:1.0'"
+ )
+ }
+
+ if (publishJavaLib) {
+ TestFileUtils.appendToFile(project.getSubproject(":javaLib").buildFile,
+ """
+ publishing {
+ repositories {
+ maven {
+ url = uri("../testrepo")
+ }
+ }
+ publications {
+ release(MavenPublication) {
+ from components.java
+ groupId = 'com.example.javaLib'
+ artifactId = 'javaLib'
+ version = '1.0'
+ }
+ }
+ }
+
+ // required for testFixtures publishing
+ group = 'com.example.javaLib'
+ """.trimIndent()
+ )
+ }
+
+ if (publishAndroidLib) {
+ TestFileUtils.appendToFile(project.getSubproject(":lib").buildFile,
+ """
+ android {
+ publishing {
+ singleVariant("release")
+ }
+ }
+
+ afterEvaluate {
+ publishing {
+ repositories {
+ maven {
+ url = uri("../testrepo")
+ }
+ }
+ publications {
+ release(MavenPublication) {
+ from components.release
+ groupId = 'com.example.lib'
+ artifactId = 'lib'
+ version = '1.0'
+ }
+ }
+ }
+ }
+
+ // required for testFixtures publishing
+ group = 'com.example.lib'
+ """.trimIndent()
+ )
+ }
+
+ if (publishJavaLib) {
+ project.executor()
+ .run(":javaLib:publish")
+ }
+
+ if (publishAndroidLib) {
+ project.executor()
+ .run(":lib:publish")
+ }
+ }
+
+ @Test
+ fun `library consumes local test fixtures`() {
+ project.executor()
+ .run(":lib:testDebugUnitTest")
+ }
+
+ @Test
+ fun `verify library test fixtures resources dependency on main resources`() {
+ project.executor()
+ .run(":lib:verifyReleaseTestFixturesResources")
+ }
+
+ @Test
+ fun `verify library resources dependency on test fixtures resources from local project`() {
+ setUpProject(
+ publishJavaLib = false,
+ publishAndroidLib = false
+ )
+ project.executor()
+ .run(":lib2:verifyReleaseResources")
+ }
+
+ @Test
+ fun `verify library resources dependency on test fixtures resources from published lib`() {
+ setUpProject(
+ publishJavaLib = false,
+ publishAndroidLib = true
+ )
+ project.executor()
+ .run(":lib2:verifyReleaseResources")
+ }
+
+ @Test
+ fun `verify library test dependency on test fixtures resources from local project`() {
+ setUpProject(
+ publishJavaLib = false,
+ publishAndroidLib = false
+ )
+ project.executor()
+ .run(":lib2:testDebugUnitTest")
+ }
+
+ @Test
+ fun `verify library test dependency on test fixtures resources from published lib`() {
+ setUpProject(
+ publishJavaLib = false,
+ publishAndroidLib = true
+ )
+ project.executor()
+ .run(":lib2:testDebugUnitTest")
+ }
+
+ @Test
+ fun `app consumes local, java and android library test fixtures`() {
+ setUpProject(
+ publishJavaLib = false,
+ publishAndroidLib = false
+ )
+ project.executor()
+ .run(":app:testDebugUnitTest")
+ }
+
+ @Test
+ fun `app consumes local, published java and android library test fixtures`() {
+ setUpProject(
+ publishJavaLib = true,
+ publishAndroidLib = true
+ )
+ project.executor()
+ .run(":app:testDebugUnitTest")
+ }
+
+ @Test
+ fun `app consumes android library test fixtures published using new publishing dsl`() {
+ setUpProject(
+ publishJavaLib = false,
+ publishAndroidLib = true
+ )
+ project.executor()
+ .run(":app:testDebugUnitTest")
+ }
+
+ @Test
+ fun `publish android library main variant without its test fixtures`() {
+ TestFileUtils.appendToFile(
+ project.getSubproject(":lib").buildFile,
+ """
+
+ afterEvaluate {
+ components.release.withVariantsFromConfiguration(
+ configurations.releaseTestFixturesVariantReleaseApiPublication) { skip() }
+ components.release.withVariantsFromConfiguration(
+ configurations.releaseTestFixturesVariantReleaseRuntimePublication) { skip() }
+ }
+
+ """.trimIndent()
+ )
+ setUpProject(
+ publishAndroidLib = true,
+ publishJavaLib = true
+ )
+ val testFixtureAar = "testrepo/com/example/lib/lib/1.0/lib-1.0-test-fixtures.aar"
+ val mainVariantAar = "testrepo/com/example/lib/lib/1.0/lib-1.0.aar"
+ assertThat(project.projectDir.resolve(testFixtureAar)).doesNotExist()
+ assertThat(project.projectDir.resolve(mainVariantAar)).exists()
+ }
+
+ @Test
+ fun `lint analyzes local and library module testFixtures sources`() {
+ setUpProjectForLint(
+ ignoreTestFixturesSourcesInApp = false
+ )
+ setUpProject(
+ publishAndroidLib = false,
+ publishJavaLib = false
+ )
+ project.executor().run(":app:lintRelease")
+ val reportFile = File(project.getSubproject("app").projectDir, "lint-results.txt")
+ assertThat(reportFile).exists()
+ assertThat(reportFile).containsAllOf(
+ "AppInterfaceTester.kt:21: Error: STOPSHIP comment found;",
+ "LibResourcesTester.java:35: Error: Missing permissions required by LibResourcesTester.methodWithUnavailablePermission: android.permission.ACCESS_COARSE_LOCATION [MissingPermission]"
+ )
+ }
+
+ @Test
+ fun `lint ignores local testFixtures sources`() {
+ setUpProjectForLint(
+ ignoreTestFixturesSourcesInApp = true
+ )
+ setUpProject(
+ publishAndroidLib = false,
+ publishJavaLib = false
+ )
+ project.executor().run(":app:lintRelease")
+ val reportFile = File(project.getSubproject("app").projectDir, "lint-results.txt")
+ assertThat(reportFile).exists()
+ assertThat(reportFile).containsAllOf(
+ "LibResourcesTester.java:35: Error: Missing permissions required by LibResourcesTester.methodWithUnavailablePermission: android.permission.ACCESS_COARSE_LOCATION [MissingPermission]"
+ )
+ }
+
+ @Test
+ fun `test plugin consumes test fixtures`() {
+ setUpProject(
+ publishAndroidLib = false,
+ publishJavaLib = false
+ )
+ useAndroidX()
+
+ project.executor().run(":appTests:packageDebug")
+
+ testExclusionInTestApk(
+ testApk = project.getSubproject(":appTests").getApk(GradleTestProject.ApkType.DEBUG),
+ expectLibAndJavaLibClassesToBeIncluded = true,
+ )
+ }
+
+ @Test
+ fun `test plugin consumes published test fixtures`() {
+ setUpProject(
+ publishAndroidLib = true,
+ publishJavaLib = true
+ )
+ useAndroidX()
+
+ project.executor().run(":appTests:packageDebug")
+
+ testExclusionInTestApk(
+ testApk = project.getSubproject(":appTests").getApk(GradleTestProject.ApkType.DEBUG),
+ expectLibAndJavaLibClassesToBeIncluded = true,
+ )
+ }
+
+ @Test
+ fun `test plugin excludes main lib classes but includes test fixtures`() {
+ setUpProject(
+ publishAndroidLib = false,
+ publishJavaLib = false
+ )
+ useAndroidX()
+
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":app").buildFile,
+ "compileOnly",
+ "implementation"
+ )
+
+ project.executor().run(":appTests:packageDebug")
+
+ testExclusionInTestApk(
+ testApk = project.getSubproject(":appTests").getApk(GradleTestProject.ApkType.DEBUG),
+ expectLibAndJavaLibClassesToBeIncluded = false,
+ )
+ }
+
+ @Test
+ fun `instrumentation tests consume test fixtures`() {
+ setUpProject(
+ publishAndroidLib = false,
+ publishJavaLib = false
+ )
+ useAndroidX()
+
+ project.executor().run(":app:packageDebugAndroidTest")
+
+ testExclusionInTestApk(
+ testApk = project.getSubproject(":app").getApk(GradleTestProject.ApkType.ANDROIDTEST_DEBUG),
+ expectLibAndJavaLibClassesToBeIncluded = true,
+ )
+ }
+
+ @Test
+ fun `instrumentation tests consume published test fixtures`() {
+ setUpProject(
+ publishAndroidLib = true,
+ publishJavaLib = true
+ )
+ useAndroidX()
+
+ project.executor().run(":app:packageDebugAndroidTest")
+
+ testExclusionInTestApk(
+ testApk = project.getSubproject(":app").getApk(GradleTestProject.ApkType.ANDROIDTEST_DEBUG),
+ expectLibAndJavaLibClassesToBeIncluded = true,
+ )
+ }
+
+ @Test
+ fun `instrumentation tests exclude main lib classes but include test fixtures`() {
+ setUpProject(
+ publishAndroidLib = false,
+ publishJavaLib = false
+ )
+ useAndroidX()
+
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":app").buildFile,
+ "compileOnly",
+ "implementation"
+ )
+
+ project.executor().run(":app:packageDebugAndroidTest")
+
+ testExclusionInTestApk(
+ testApk = project.getSubproject(":app").getApk(GradleTestProject.ApkType.ANDROIDTEST_DEBUG),
+ expectLibAndJavaLibClassesToBeIncluded = false,
+ )
+ }
+
+ @Test
+ fun `test kotlin stdlib missing`() {
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":app").buildFile,
+ "testFixturesImplementation \"org.jetbrains.kotlin:kotlin-stdlib:\${libs.versions.kotlinVersion.get()}\"",
+ ""
+ )
+ val result = project.executor().expectFailure().run(":app:testDebugUnitTest")
+ result.assertErrorContains("Kotlin standard library is missing")
+ }
+
+ @Test
+ fun `test kotlin version too low`() {
+ Assume.assumeTrue(kotlinVersion == TestUtils.KOTLIN_VERSION_FOR_TESTS)
+ TestFileUtils.searchAndReplace(
+ project.projectDir.parentFile.resolve(VERSION_CATALOG),
+ "version('kotlinVersion', '$kotlinVersion' )",
+ "version('kotlinVersion', '1.8.10' )"
+ )
+ val result = project.executor().expectFailure().run(":app:testDebugUnitTest")
+ result.assertErrorContains(
+ "The current Kotlin Gradle plugin version (1.8.10) is below the required"
+ )
+ }
+
+ private fun testExclusionInTestApk(
+ testApk: Apk,
+ expectTestFixturesClassesToBeIncluded: Boolean = true,
+ expectLibAndJavaLibClassesToBeIncluded: Boolean,
+ expectAppClassesToBeIncluded: Boolean = false
+ ) {
+ testApk.use {
+ it.mainDexFile.get().classes.keys.let { classes ->
+ // test fixtures classes
+ listOf(
+ "Lcom/example/app/testFixtures/AppInterfaceTester;",
+ "Lcom/example/javalib/testFixtures/JavaLibInterfaceTester;",
+ "Lcom/example/lib/testFixtures/LibInterfaceTester;",
+ "Lcom/example/lib/testFixtures/LibResourcesTester;"
+ ).forEach { clazz ->
+ if (expectTestFixturesClassesToBeIncluded) {
+ Truth.assertThat(classes).contains(clazz)
+ } else {
+ Truth.assertThat(classes).doesNotContain(clazz)
+ }
+ }
+
+ // lib and java lib classes
+ listOf(
+ "Lcom/example/javalib/JavaLibInterface;",
+ "Lcom/example/lib/LibInterface;",
+ ).forEach { clazz ->
+ if (expectLibAndJavaLibClassesToBeIncluded) {
+ Truth.assertThat(classes).contains(clazz)
+ } else {
+ Truth.assertThat(classes).doesNotContain(clazz)
+ }
+ }
+
+ // app classes
+ listOf(
+ "Lcom/example/app/AppInterface;"
+ ).forEach { clazz ->
+ if (expectAppClassesToBeIncluded) {
+ Truth.assertThat(classes).contains(clazz)
+ } else {
+ Truth.assertThat(classes).doesNotContain(clazz)
+ }
+ }
+ }
+ }
+ }
+
+ private fun setUpProjectForLint(ignoreTestFixturesSourcesInApp: Boolean) {
+ project.getSubproject(":app").buildFile.appendText(
+ """
+ android {
+ testBuildType "release"
+ lint {
+ abortOnError false
+ enable 'StopShip'
+ textOutput file("lint-results.txt")
+ checkDependencies true
+ ignoreTestFixturesSources $ignoreTestFixturesSourcesInApp
+ }
+ }
+ """.trimIndent()
+ )
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":app")
+ .file("src/testFixtures/java/com/example/app/testFixtures/AppInterfaceTester.kt"),
+ "class AppInterfaceTester(private val name: String) {",
+ "// STOPSHIP\n" + "class AppInterfaceTester(private val name: String) {"
+ )
+ project.getSubproject(":lib").buildFile.appendText(
+ """
+ android {
+ testBuildType "release"
+ }
+
+ dependencies {
+ testFixturesApi 'androidx.annotation:annotation:1.1.0'
+ }
+ """.trimIndent()
+ )
+ TestFileUtils.searchAndReplace(
+ project.getSubproject(":lib")
+ .file("src/testFixtures/java/com/example/lib/testFixtures/LibResourcesTester.java"),
+ "public void test() {",
+ """
+ @androidx.annotation.RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ public void methodWithUnavailablePermission() {
+ }
+
+ public void test() {
+ methodWithUnavailablePermission();
+ """.trimIndent()
+ )
+ useAndroidX()
+ }
+
+ private fun useAndroidX() {
+ TestFileUtils.appendToFile(project.file("gradle.properties"), "android.useAndroidX=true")
+ }
+}
diff --git a/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/GradleTestProject.kt b/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/GradleTestProject.kt
index 7be5de6..e936da6 100644
--- a/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/GradleTestProject.kt
+++ b/build-system/integration-test/framework/src/main/java/com/android/build/gradle/integration/common/fixture/GradleTestProject.kt
@@ -162,7 +162,7 @@
internal const val COMMON_LOCAL_REPO = "commonLocalRepo.gradle"
private const val COMMON_BUILD_SCRIPT = "commonBuildScript.gradle"
private const val COMMON_VERSIONS = "commonVersions.gradle"
- private const val VERSION_CATALOG = "versionCatalog.gradle"
+ const val VERSION_CATALOG = "versionCatalog.gradle"
const val DEFAULT_TEST_PROJECT_NAME = "project"
@JvmStatic
diff --git a/build-system/integration-test/test-projects/composeHelloWorld/app/build.gradle b/build-system/integration-test/test-projects/composeHelloWorld/app/build.gradle
index 8796163..06366ec 100644
--- a/build-system/integration-test/test-projects/composeHelloWorld/app/build.gradle
+++ b/build-system/integration-test/test-projects/composeHelloWorld/app/build.gradle
@@ -35,6 +35,10 @@
composeOptions {
kotlinCompilerExtensionVersion = "${libs.versions.composeCompilerVersion.get()}"
}
+ testFixtures {
+ enable = true
+ }
+ experimentalProperties["android.experimental.enableScreenshotTest"] = true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
@@ -51,4 +55,10 @@
androidTestImplementation 'androidx.test.ext:junit:1.1.3-alpha02'
androidTestImplementation 'androidx.test:rules:1.4.0-alpha06'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+ testFixturesImplementation "org.jetbrains.kotlin:kotlin-stdlib:${libs.versions.kotlinVersionForCompose.get()}"
+ testFixturesImplementation "androidx.activity:activity-compose:1.5.1"
+ testFixturesImplementation "androidx.compose.ui:ui:${libs.versions.composeVersion.get()}"
+ testFixturesImplementation "androidx.compose.material:material:${libs.versions.composeVersion.get()}"
+ testFixturesImplementation "androidx.compose.ui:ui-tooling:${libs.versions.composeVersion.get()}"
}
diff --git a/build-system/integration-test/test-projects/composeHelloWorld/app/src/screenshotTest/java/com/example/helloworldcompose/ScreenshotTest.kt b/build-system/integration-test/test-projects/composeHelloWorld/app/src/screenshotTest/java/com/example/helloworldcompose/ScreenshotTest.kt
new file mode 100644
index 0000000..26dbfac
--- /dev/null
+++ b/build-system/integration-test/test-projects/composeHelloWorld/app/src/screenshotTest/java/com/example/helloworldcompose/ScreenshotTest.kt
@@ -0,0 +1,24 @@
+package com.example.helloworldcompose
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun HelloWorldColumn() {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text("Hello, World1")
+ Text("Hello, World2")
+ Text("Hello, World3")
+ }
+}
+
+@Preview
+@Composable
+fun PreviewGreeting() {
+ HelloWorldColumn()
+}
diff --git a/build-system/integration-test/test-projects/composeHelloWorld/app/src/testFixtures/java/com/example/helloworldcompose/Fixture.kt b/build-system/integration-test/test-projects/composeHelloWorld/app/src/testFixtures/java/com/example/helloworldcompose/Fixture.kt
new file mode 100644
index 0000000..26dbfac
--- /dev/null
+++ b/build-system/integration-test/test-projects/composeHelloWorld/app/src/testFixtures/java/com/example/helloworldcompose/Fixture.kt
@@ -0,0 +1,24 @@
+package com.example.helloworldcompose
+
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+@Composable
+fun HelloWorldColumn() {
+ Column(modifier = Modifier.padding(16.dp)) {
+ Text("Hello, World1")
+ Text("Hello, World2")
+ Text("Hello, World3")
+ }
+}
+
+@Preview
+@Composable
+fun PreviewGreeting() {
+ HelloWorldColumn()
+}
diff --git a/build-system/integration-test/test-projects/composeHelloWorld/gradle.properties b/build-system/integration-test/test-projects/composeHelloWorld/gradle.properties
index 6ce64b0..767fc7d 100644
--- a/build-system/integration-test/test-projects/composeHelloWorld/gradle.properties
+++ b/build-system/integration-test/test-projects/composeHelloWorld/gradle.properties
@@ -14,4 +14,6 @@
# limitations under the License.
#
-android.useAndroidX=true
\ No newline at end of file
+android.useAndroidX=true
+android.experimental.enableScreenshotTest=true
+android.experimental.enableTestFixturesKotlinSupport=true
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/build.gradle b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/build.gradle
new file mode 100644
index 0000000..48f330e
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+plugins {
+ id 'com.android.application'
+ id 'kotlin-android'
+}
+
+android {
+ namespace "com.example.app"
+ compileSdkVersion libs.versions.latestCompileSdk.get().toInteger()
+ buildToolsVersion = libs.versions.buildToolsVersion.get()
+
+ defaultConfig {
+ minSdkVersion libs.versions.supportLibMinSdk.get()
+ }
+
+ lintOptions {
+ checkReleaseBuilds false
+ }
+
+ testFixtures.enable = true
+}
+
+kotlin {
+ jvmToolchain(17)
+}
+
+dependencies {
+ compileOnly project(":javaLib")
+ compileOnly project(":lib")
+
+ testFixturesApi 'com.google.truth:truth:0.44'
+ testFixturesImplementation "org.jetbrains.kotlin:kotlin-stdlib:${libs.versions.kotlinVersion.get()}"
+
+ testImplementation 'junit:junit:4.12'
+ testImplementation testFixtures(project(":javaLib"))
+ testImplementation testFixtures(project(":lib"))
+
+ androidTestImplementation testFixtures(project(":javaLib"))
+ androidTestImplementation testFixtures(project(":lib"))
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5c3d365
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest/>
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/AppInterface.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/AppInterface.kt
new file mode 100644
index 0000000..7837fdc
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/AppInterface.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 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 com.example.app
+
+interface AppInterface {
+ val name: String?
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/JavaLibInterfaceImpl.java b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/JavaLibInterfaceImpl.java
new file mode 100644
index 0000000..1908301
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/JavaLibInterfaceImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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 com.example.app;
+
+import com.example.javalib.JavaLibInterface;
+
+public class JavaLibInterfaceImpl implements JavaLibInterface {
+ private int id;
+
+ public JavaLibInterfaceImpl(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public int getId() {
+ return id;
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/LibInterfaceImpl.java b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/LibInterfaceImpl.java
new file mode 100644
index 0000000..ecb22b5
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/main/java/com/example/app/LibInterfaceImpl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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 com.example.app;
+
+import com.example.lib.LibInterface;
+
+public class LibInterfaceImpl implements LibInterface {
+ private String name;
+
+ public LibInterfaceImpl(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/test/java/com/example/test/UnitTest.java b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/test/java/com/example/test/UnitTest.java
new file mode 100644
index 0000000..c18cfcf
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/test/java/com/example/test/UnitTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2024 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 com.example.test;
+
+import com.example.app.AppInterface;
+import com.example.app.JavaLibInterfaceImpl;
+import com.example.app.LibInterfaceImpl;
+import com.example.app.testFixtures.AppInterfaceTester;
+import com.example.javalib.testFixtures.JavaLibInterfaceTester;
+import com.example.lib.testFixtures.LibInterfaceTester;
+import org.junit.Test;
+
+public class UnitTest {
+
+ @Test
+ public void testAndroidLibTestFixturesDependency() {
+ String name = "test";
+ LibInterfaceTester tester = new LibInterfaceTester(name);
+ tester.test(new LibInterfaceImpl(name));
+ }
+
+ @Test
+ public void testJavaLibTestFixturesDependency() {
+ int id = 1234;
+ JavaLibInterfaceTester tester = new JavaLibInterfaceTester(id);
+ tester.test(new JavaLibInterfaceImpl(id));
+ }
+
+ @Test
+ public void testLocalAppTestFixturesDependency() {
+ AppInterfaceTester tester = new AppInterfaceTester("test");
+ tester.test(new AppInterfaceImpl());
+ }
+
+ private class AppInterfaceImpl implements AppInterface {
+ @Override
+ public String getName() {
+ return "test";
+ }
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/test/java/com/example/test/UnitTestKotlin.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/test/java/com/example/test/UnitTestKotlin.kt
new file mode 100644
index 0000000..ea97285
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/test/java/com/example/test/UnitTestKotlin.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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 com.example.test
+
+import com.example.app.AppInterface
+import com.example.app.JavaLibInterfaceImpl
+import com.example.app.LibInterfaceImpl
+import com.example.app.testFixtures.AppInterfaceTester
+import com.example.javalib.testFixtures.JavaLibInterfaceTester
+import com.example.lib.testFixtures.LibInterfaceTester
+import org.junit.Test
+
+class UnitTestKotlin {
+ @Test
+ fun testAndroidLibTestFixturesDependency() {
+ val name = "test"
+ val tester: LibInterfaceTester = LibInterfaceTester(name)
+ tester.test(LibInterfaceImpl(name))
+ }
+
+ @Test
+ fun testJavaLibTestFixturesDependency() {
+ val id = 1234
+ val tester: JavaLibInterfaceTester = JavaLibInterfaceTester(id)
+ tester.test(JavaLibInterfaceImpl(id))
+ }
+
+ @Test
+ fun testLocalAppTestFixturesDependency() {
+ val tester: AppInterfaceTester = AppInterfaceTester("test")
+ tester.test(AppInterfaceImpl())
+ }
+
+ private inner class AppInterfaceImpl : AppInterface {
+ override val name = "test"
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/testFixtures/java/com/example/app/testFixtures/AppInterfaceTester.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/testFixtures/java/com/example/app/testFixtures/AppInterfaceTester.kt
new file mode 100644
index 0000000..e5ac1c8
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/app/src/testFixtures/java/com/example/app/testFixtures/AppInterfaceTester.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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 com.example.app.testFixtures
+
+import com.example.app.AppInterface
+import com.google.common.truth.Truth
+
+class AppInterfaceTester(private val name: String) {
+ fun test(`object`: AppInterface) {
+ Truth.assertThat(`object`.name).isEqualTo(name)
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/appTests/build.gradle b/build-system/integration-test/test-projects/testFixturesKotlinApp/appTests/build.gradle
new file mode 100644
index 0000000..456cb2b
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/appTests/build.gradle
@@ -0,0 +1,24 @@
+plugins {
+ id 'com.android.test'
+}
+
+android {
+ namespace = "com.example.apptests"
+ compileSdkVersion libs.versions.latestCompileSdk.get().toInteger()
+ buildToolsVersion = libs.versions.buildToolsVersion.get()
+
+ targetProjectPath ':app'
+
+ defaultConfig {
+ minSdkVersion libs.versions.supportLibMinSdk.get()
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+}
+
+dependencies {
+ implementation testFixtures(project(":app"))
+ implementation testFixtures(project(":javaLib"))
+ implementation testFixtures(project(":lib"))
+
+ implementation 'junit:junit:4.12'
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/appTests/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/testFixturesKotlinApp/appTests/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5c3d365
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/appTests/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest/>
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/build.gradle b/build-system/integration-test/test-projects/testFixturesKotlinApp/build.gradle
new file mode 100644
index 0000000..583a331
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/build.gradle
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+apply from: "../commonHeader.gradle"
+buildscript {
+ apply from: "../commonHeader.gradle" // for $kotlinVersion
+ apply from: "../commonBuildScript.gradle"
+
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${libs.versions.kotlinVersion.get()}"
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/gradle.properties b/build-system/integration-test/test-projects/testFixturesKotlinApp/gradle.properties
new file mode 100644
index 0000000..0e3ef84
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/gradle.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2024 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.
+#
+
+android.experimental.enableTestFixturesKotlinSupport=true
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/build.gradle b/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/build.gradle
new file mode 100644
index 0000000..da7c6a7
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/build.gradle
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+plugins {
+ id 'java-library'
+ id 'java-test-fixtures'
+ id 'maven-publish'
+ id 'kotlin'
+}
+
+dependencies {
+ testFixturesApi 'com.google.truth:truth:0.44'
+}
+
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/src/main/java/com/example/javalib/JavaLibInterface.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/src/main/java/com/example/javalib/JavaLibInterface.kt
new file mode 100644
index 0000000..ab57295
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/src/main/java/com/example/javalib/JavaLibInterface.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 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 com.example.javalib
+
+interface JavaLibInterface {
+ val id: Int
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/src/testFixtures/java/com/example/javalib/testFixtures/JavaLibInterfaceTester.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/src/testFixtures/java/com/example/javalib/testFixtures/JavaLibInterfaceTester.kt
new file mode 100644
index 0000000..435fb0e
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/javaLib/src/testFixtures/java/com/example/javalib/testFixtures/JavaLibInterfaceTester.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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 com.example.javalib.testFixtures
+
+import com.example.javalib.JavaLibInterface
+import com.google.common.truth.Truth
+
+class JavaLibInterfaceTester(private val id: Int) {
+ fun test(`object`: JavaLibInterface) {
+ Truth.assertThat(`object`.id).isEqualTo(id)
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/build.gradle b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/build.gradle
new file mode 100644
index 0000000..a11cdd6
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/build.gradle
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'maven-publish'
+ id 'kotlin-android'
+}
+
+android {
+ namespace "com.example.lib"
+ compileSdkVersion libs.versions.latestCompileSdk.get().toInteger()
+ testFixtures {
+ enable true
+ androidResources true
+ }
+}
+
+kotlin {
+ jvmToolchain(17)
+}
+
+dependencies {
+ testFixturesApi 'com.google.truth:truth:0.44'
+ testFixturesImplementation "org.jetbrains.kotlin:kotlin-stdlib:${libs.versions.kotlinVersion.get()}"
+ testImplementation 'junit:junit:4.12'
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5c3d365
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest/>
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/java/com/example/lib/LibInterface.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/java/com/example/lib/LibInterface.kt
new file mode 100644
index 0000000..3339510
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/java/com/example/lib/LibInterface.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2024 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 com.example.lib
+
+interface LibInterface {
+ val name: String?
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/res/values/strings.xml
new file mode 100644
index 0000000..340f38f
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="libResourceString">lib resource string</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/test/java/com/example/test/UnitTest.java b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/test/java/com/example/test/UnitTest.java
new file mode 100644
index 0000000..e08659c
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/test/java/com/example/test/UnitTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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 com.example.test;
+
+import com.example.lib.LibInterface;
+import com.example.lib.testFixtures.LibInterfaceTester;
+import com.example.lib.testFixtures.LibResourcesTester;
+import org.junit.Test;
+
+public class UnitTest {
+ private class LibInterfaceImpl implements LibInterface {
+ @Override
+ public String getName() {
+ return "test";
+ }
+ }
+
+ @Test
+ public void testLibInterfaceTester() {
+ LibInterfaceTester tester = new LibInterfaceTester("test");
+ tester.test(new LibInterfaceImpl());
+ }
+
+ @Test
+ public void testLibResourcesTester() {
+ LibResourcesTester tester =
+ new LibResourcesTester(
+ com.example.lib.R.string.libResourceString,
+ com.example.lib.testFixtures.R.string.testFixturesResourceString);
+ tester.test();
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/test/java/com/example/test/UnitTestKotlin.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/test/java/com/example/test/UnitTestKotlin.kt
new file mode 100644
index 0000000..4d784b1
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/test/java/com/example/test/UnitTestKotlin.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 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 com.example.test
+
+import com.example.lib.LibInterface
+import com.example.lib.testFixtures.LibInterfaceTester
+import com.example.lib.testFixtures.LibResourcesTester
+import org.junit.Test
+
+class UnitTestKotlin {
+ private inner class LibInterfaceImpl : LibInterface {
+ override val name = "test"
+ }
+
+ @Test
+ fun testLibInterfaceTester() {
+ val tester: LibInterfaceTester = LibInterfaceTester("test")
+ tester.test(LibInterfaceImpl())
+ }
+
+ @Test
+ fun testLibResourcesTester() {
+ val tester: LibResourcesTester =
+ LibResourcesTester(
+ com.example.lib.R.string.libResourceString,
+ com.example.lib.testFixtures.R.string.testFixturesResourceString
+ )
+ tester.test()
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/java/com/example/lib/testFixtures/LibInterfaceTester.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/java/com/example/lib/testFixtures/LibInterfaceTester.kt
new file mode 100644
index 0000000..0b17012
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/java/com/example/lib/testFixtures/LibInterfaceTester.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 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 com.example.lib.testFixtures
+
+import com.example.lib.LibInterface
+import com.google.common.truth.Truth
+
+class LibInterfaceTester(private val name: String) {
+ fun test(`object`: LibInterface) {
+ Truth.assertThat(`object`.name).isEqualTo(name)
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/java/com/example/lib/testFixtures/LibResourcesTester.java b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/java/com/example/lib/testFixtures/LibResourcesTester.java
new file mode 100644
index 0000000..97c15e0
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/java/com/example/lib/testFixtures/LibResourcesTester.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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 com.example.lib.testFixtures;
+
+import com.google.common.truth.Truth;
+
+public class LibResourcesTester {
+ private int libResourceId;
+ private int testFixturesResourceId;
+
+ public LibResourcesTester(int libResourceId, int testFixturesResourceId) {
+ this.libResourceId = libResourceId;
+ this.testFixturesResourceId = testFixturesResourceId;
+ }
+
+ public void test() {
+ Truth.assertThat(com.example.lib.R.string.libResourceString).isEqualTo(this.libResourceId);
+ Truth.assertThat(com.example.lib.testFixtures.R.string.testFixturesResourceString)
+ .isEqualTo(this.testFixturesResourceId);
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/res/values/strings.xml b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/res/values/strings.xml
new file mode 100644
index 0000000..8caab62
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib/src/testFixtures/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="testFixturesResourceString">@string/libResourceString</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/build.gradle b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/build.gradle
new file mode 100644
index 0000000..ecab548
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/build.gradle
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+plugins {
+ id 'com.android.library'
+ id 'kotlin-android'
+}
+
+android {
+ namespace "com.example.lib2"
+ compileSdkVersion libs.versions.latestCompileSdk.get().toInteger()
+}
+
+kotlin {
+ jvmToolchain(17)
+}
+
+dependencies {
+ implementation testFixtures(project(":lib"))
+
+ testImplementation 'junit:junit:4.12'
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/main/AndroidManifest.xml b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5c3d365
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest/>
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/main/res/values/strings.xml b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/main/res/values/strings.xml
new file mode 100644
index 0000000..6df44c7
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/main/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2024 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.
+ -->
+
+<resources>
+ <string name="referenceToLibResourceString">@string/libResourceString</string>
+ <string name="referenceToTestFixturesResourceString">@string/testFixturesResourceString</string>
+</resources>
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/test/java/com/example/test/UnitTest.java b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/test/java/com/example/test/UnitTest.java
new file mode 100644
index 0000000..b20ee5f
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/test/java/com/example/test/UnitTest.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2024 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 com.example.test;
+
+import com.example.lib.testFixtures.LibResourcesTester;
+import org.junit.Test;
+
+public class UnitTest {
+
+ @Test
+ public void testTestFixturesResourcesDependency() {
+ LibResourcesTester tester =
+ new LibResourcesTester(
+ com.example.lib.R.string.libResourceString,
+ com.example.lib.testFixtures.R.string.testFixturesResourceString);
+ tester.test();
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/test/java/com/example/test/UnitTestKotlin.kt b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/test/java/com/example/test/UnitTestKotlin.kt
new file mode 100644
index 0000000..c84de65
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/lib2/src/test/java/com/example/test/UnitTestKotlin.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 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 com.example.test
+
+import com.example.lib.testFixtures.LibResourcesTester
+import org.junit.Test
+
+class UnitTestKotlin {
+ @Test
+ fun testTestFixturesResourcesDependency() {
+ val tester: LibResourcesTester =
+ LibResourcesTester(
+ com.example.lib.R.string.libResourceString,
+ com.example.lib.testFixtures.R.string.testFixturesResourceString
+ )
+ tester.test()
+ }
+}
diff --git a/build-system/integration-test/test-projects/testFixturesKotlinApp/settings.gradle b/build-system/integration-test/test-projects/testFixturesKotlinApp/settings.gradle
new file mode 100644
index 0000000..5dacde2
--- /dev/null
+++ b/build-system/integration-test/test-projects/testFixturesKotlinApp/settings.gradle
@@ -0,0 +1,13 @@
+include ':app'
+include ':appTests'
+include ':lib'
+include ':lib2'
+include ':javaLib'
+
+dependencyResolutionManagement {
+ repositories {
+ maven {
+ url = 'testrepo'
+ }
+ }
+}