diff --git a/android-library-no-tests/build.gradle.kts b/android-library-no-tests/build.gradle.kts index b71a13a8..76dda11f 100644 --- a/android-library-no-tests/build.gradle.kts +++ b/android-library-no-tests/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("com.android.library") - kotlin("android") } android { diff --git a/build.gradle b/build.gradle index a3cabd9d..165b5226 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,6 @@ buildscript { plugins { alias(libs.plugins.agp) apply false - alias(libs.plugins.kgp) apply false alias(libs.plugins.ben.manes.versions) id "com.osacky.fulladle" alias(libs.plugins.kotlinter) @@ -22,7 +21,7 @@ fladle { } tasks.wrapper.configure { - gradleVersion = '8.14.3' + gradleVersion = '9.1.0' } def isNonStable = { String version -> diff --git a/fladle-plugin/build.gradle.kts b/fladle-plugin/build.gradle.kts index b0fe4eb1..29d4e847 100644 --- a/fladle-plugin/build.gradle.kts +++ b/fladle-plugin/build.gradle.kts @@ -18,18 +18,18 @@ plugins { alias(libs.plugins.vanniktech.publish) } -// See https://github.com/slackhq/keeper/pull/11#issuecomment-579544375 for context -val isReleaseMode : Boolean = hasProperty("fladle.releaseMode") - dependencies { compileOnly(gradleApi()) - if (isReleaseMode) { - compileOnly(libs.agp) - } else { - implementation(libs.agp) + compileOnly(libs.agp) { + exclude(group = "org.jetbrains.kotlin", module = "kotlin-compiler-embeddable") + exclude(group = "org.jetbrains.kotlin", module = "kotlin-compiler-runner") } compileOnly(libs.gradle.enterprise) + // AGP must be on the runtime classpath so GradleTestKit's withPluginClasspath() + // can resolve the com.android.application and com.android.library plugins. + runtimeOnly(libs.agp) + testImplementation(gradleTestKit()) testImplementation(libs.junit) testImplementation(libs.truth) @@ -106,8 +106,8 @@ tasks.withType(ValidatePlugins::class.java).configureEach { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile::class.java).configureEach { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) - languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_7) - apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_1_7) + languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0) + apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_0) } } diff --git a/fladle-plugin/settings.gradle.kts b/fladle-plugin/settings.gradle.kts index 528a0fa6..09608f5d 100644 --- a/fladle-plugin/settings.gradle.kts +++ b/fladle-plugin/settings.gradle.kts @@ -1,5 +1,9 @@ rootProject.name = "fladle" +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} + dependencyResolutionManagement { versionCatalogs { create("libs") { diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladleConfig.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladleConfig.kt index 869a9938..d6721998 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladleConfig.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladleConfig.kt @@ -51,13 +51,11 @@ interface FladleConfig { @get:Input val testTargets: ListProperty + // The maximum number of shards. Value will be overwritten by [maxTestShards] if both used in configuration @Deprecated( message = "testShards is deprecated. Use maxTestShards instead", replaceWith = ReplaceWith("maxTestShards"), ) - /** - * The maximum number of shards. Value will be overwritten by [maxTestShards] if both used in configuration - */ @get:Input @get:Optional val testShards: Property @@ -473,7 +471,8 @@ interface FladleConfig { @Internal fun getPresentProperties() = - this::class.memberProperties + this::class + .memberProperties .filter { when (val prop = it.call(this)) { is Property<*> -> prop.isPresent diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladlePluginDelegate.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladlePluginDelegate.kt index 6d3c1f92..249a2414 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladlePluginDelegate.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FladlePluginDelegate.kt @@ -1,14 +1,15 @@ package com.osacky.flank.gradle -import com.android.build.gradle.AppExtension -import com.android.build.gradle.TestedExtension -import com.android.build.gradle.internal.tasks.factory.dependsOn -import com.android.builder.model.TestOptions +import com.android.build.api.dsl.ApplicationExtension +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.FilterConfiguration +import com.android.build.api.variant.HasAndroidTest import com.osacky.flank.gradle.validation.checkForExclusionUsage import com.osacky.flank.gradle.validation.validateOptionsUsed import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.gradle.api.plugins.BasePluginExtension import org.gradle.api.tasks.TaskContainer import org.gradle.kotlin.dsl.create import org.gradle.util.GradleVersion @@ -24,7 +25,10 @@ class FladlePluginDelegate { target.tasks.register("flankAuth", FlankJavaExec::class.java) { doFirst { - target.layout.fladleDir.get().asFile.mkdirs() + target.layout.fladleDir + .get() + .asFile + .mkdirs() } classpath = project.fladleConfig args = listOf("auth", "login") @@ -34,7 +38,6 @@ class FladlePluginDelegate { } private fun checkMinimumGradleVersion() { - // Gradle 4.9 is required because we use the lazy task configuration API. if (GRADLE_MIN_VERSION > GradleVersion.current()) { throw GradleException("Fladle requires at minimum version $GRADLE_MIN_VERSION. Detected version ${GradleVersion.current()}.") } @@ -44,24 +47,22 @@ class FladlePluginDelegate { project: Project, base: FlankGradleExtension, ) { - if (GradleVersion.current() > GradleVersion.version("6.1")) { - base.flankVersion.finalizeValueOnRead() - base.flankCoordinates.finalizeValueOnRead() - base.serviceAccountCredentials.finalizeValueOnRead() + base.flankVersion.finalizeValueOnRead() + base.flankCoordinates.finalizeValueOnRead() + base.serviceAccountCredentials.finalizeValueOnRead() + + // Register onVariants callbacks before afterEvaluate for APK path detection + project.pluginManager.withPlugin("com.android.application") { + if (!base.debugApk.isPresent || !base.instrumentationApk.isPresent) { + findDebugAndInstrumentationApk(project, base) + } } + project.afterEvaluate { // Add Flank dependency to Fladle Configuration // Must be done afterEvaluate otherwise extension values will not be set. project.dependencies.add(FLADLE_CONFIG, "${base.flankCoordinates.get()}:${base.flankVersion.get()}") - // Only use automatic apk path detection for 'com.android.application' projects. - project.pluginManager.withPlugin("com.android.application") { - // This doesn't work properly for multiple configs since they likely are inheriting the config from root already. See #60 https://github.com/runningcode/fladle/issues/60 - if (!base.debugApk.isPresent || !base.instrumentationApk.isPresent) { - findDebugAndInstrumentationApk(project, base) - } - } - tasks.apply { createTasksForConfig(base, base, project, "") @@ -95,14 +96,21 @@ class FladlePluginDelegate { val writeConfigProps = register("writeConfigProps$name", YamlConfigWriterTask::class.java, base, config, name) - writeConfigProps.dependsOn(validateFladle) + writeConfigProps.configure { dependsOn(validateFladle) } register("printYml$name") { description = "Print the flank.yml file to the console." group = TASK_GROUP dependsOn(writeConfigProps) doLast { - println(writeConfigProps.get().fladleConfigFile.get().asFile.readText()) + println( + writeConfigProps + .get() + .fladleConfigFile + .get() + .asFile + .readText(), + ) } } @@ -110,7 +118,19 @@ class FladlePluginDelegate { if (useDefaultDir) setUpWorkingDir(configName) description = "Finds problems with the current configuration." classpath = project.fladleConfig - args = listOf("firebase", "test", "android", "doctor", "-c", writeConfigProps.get().fladleConfigFile.get().asFile.absolutePath) + args = + listOf( + "firebase", + "test", + "android", + "doctor", + "-c", + writeConfigProps + .get() + .fladleConfigFile + .get() + .asFile.absolutePath, + ) dependsOn(writeConfigProps) } @@ -122,13 +142,30 @@ class FladlePluginDelegate { args = if (project.hasProperty("dumpShards")) { listOf( - "firebase", "test", "android", "run", "-c", - writeConfigProps.get().fladleConfigFile.get().asFile.absolutePath, "--dump-shards", + "firebase", + "test", + "android", + "run", + "-c", + writeConfigProps + .get() + .fladleConfigFile + .get() + .asFile.absolutePath, + "--dump-shards", ) } else { listOf( - "firebase", "test", "android", "run", "-c", - writeConfigProps.get().fladleConfigFile.get().asFile.absolutePath, + "firebase", + "test", + "android", + "run", + "-c", + writeConfigProps + .get() + .fladleConfigFile + .get() + .asFile.absolutePath, ) } if (config.serviceAccountCredentials.isPresent) { @@ -136,17 +173,15 @@ class FladlePluginDelegate { } dependsOn(writeConfigProps) if (config.dependOnAssemble.isPresent && config.dependOnAssemble.get()) { - val testedExtension = - requireNotNull(project.extensions.findByType(TestedExtension::class.java)) { "Could not find TestedExtension in ${project.name}" } - testedExtension.testVariants.configureEach { - if (testedVariant.isExpectedVariant(config)) { - if (testedVariant.assembleProvider.isPresent) { - dependsOn(testedVariant.assembleProvider) - } - if (assembleProvider.isPresent) { - dependsOn(assembleProvider) - } - } + // Find assemble tasks by convention name pattern + val variantName = config.variant.orNull + if (variantName != null) { + val capitalizedVariant = variantName.capitalize() + dependsOn("assemble$capitalizedVariant") + dependsOn("assemble${capitalizedVariant}AndroidTest") + } else { + dependsOn("assembleDebug") + dependsOn("assembleDebugAndroidTest") } } if (config.localResultsDir.hasValue) { @@ -172,16 +207,17 @@ class FladlePluginDelegate { private fun automaticallyConfigureTestOrchestrator( project: Project, config: FladleConfig, - androidExtension: AppExtension, + androidExtension: ApplicationExtension, ) { project.afterEvaluate { + val execution = androidExtension.testOptions.execution.uppercase() val useOrchestrator = - androidExtension.testOptions.getExecutionEnum() == TestOptions.Execution.ANDROIDX_TEST_ORCHESTRATOR || - androidExtension.testOptions.getExecutionEnum() == TestOptions.Execution.ANDROID_TEST_ORCHESTRATOR + execution == "ANDROIDX_TEST_ORCHESTRATOR" || + execution == "ANDROID_TEST_ORCHESTRATOR" if (useOrchestrator) { log("Automatically detected the use of Android Test Orchestrator") + config.useOrchestrator.set(true) } - config.useOrchestrator.set(useOrchestrator) } } @@ -189,28 +225,75 @@ class FladlePluginDelegate { project: Project, config: FladleConfig, ) { - val baseExtension = - requireNotNull(project.extensions.findByType(AppExtension::class.java)) { "Could not find AppExtension in ${project.name}" } - automaticallyConfigureTestOrchestrator(project, config, baseExtension) - baseExtension.testVariants.configureEach { - val appVariant = testedVariant - outputs.configureEach test@{ - appVariant.outputs - .matching { it.isExpectedAbiOutput(config) } - .configureEach app@{ - if (appVariant.isExpectedVariant(config)) { - if (!config.debugApk.isPresent) { - // Don't set debug apk if not already set. #172 - project.log("Configuring fladle.debugApk from variant ${this@app.name}") - config.debugApk.set(this@app.outputFile.absolutePath) - } - if (!config.roboScript.isPresent && !config.instrumentationApk.isPresent && !config.sanityRobo.get()) { - // Don't set instrumentation apk if not already set. #172 - project.log("Configuring fladle.instrumentationApk from variant ${this@test.name}") - config.instrumentationApk.set(this@test.outputFile.absolutePath) - } - } + val androidExtension = + requireNotNull( + project.extensions.findByType(ApplicationExtension::class.java), + ) { "Could not find ApplicationExtension in ${project.name}" } + automaticallyConfigureTestOrchestrator(project, config, androidExtension) + + val androidComponents = + requireNotNull(project.extensions.findByType(ApplicationAndroidComponentsExtension::class.java)) { + "Could not find ApplicationAndroidComponentsExtension in ${project.name}" + } + + androidComponents.onVariants { variant -> + if (!variant.isExpectedVariant(config)) return@onVariants + val androidTest = (variant as? HasAndroidTest)?.androidTest ?: return@onVariants + + val buildType = variant.buildType ?: return@onVariants + val flavorName = variant.productFlavors.joinToString("") { it.second } + val flavorPath = variant.productFlavors.joinToString("/") { it.second } + val archivesName = + project.extensions + .getByType(BasePluginExtension::class.java) + .archivesName + .get() + val buildDir = project.layout.buildDirectory + + // Test APK path + val testApkDirPath = if (flavorPath.isNotEmpty()) "androidTest/$flavorPath/$buildType" else "androidTest/$buildType" + val testApkFileName = + if (flavorName.isNotEmpty()) { + "$archivesName-$flavorName-$buildType-androidTest.apk" + } else { + "$archivesName-$buildType-androidTest.apk" + } + val testApkPath = + buildDir + .file("outputs/apk/$testApkDirPath/$testApkFileName") + .get() + .asFile.absolutePath + + variant.outputs.forEach { output -> + if (!output.isExpectedAbiOutput(config)) return@forEach + + val abiFilter = output.filters.firstOrNull { it.filterType == FilterConfiguration.FilterType.ABI } + val abiName = abiFilter?.identifier + + val appApkDirPath = if (flavorPath.isNotEmpty()) "$flavorPath/$buildType" else buildType + val appApkFileName = + buildString { + append(archivesName) + if (flavorName.isNotEmpty()) append("-$flavorName") + if (abiName != null) append("-$abiName") + append("-$buildType.apk") } + val appApkPath = + buildDir + .file("outputs/apk/$appApkDirPath/$appApkFileName") + .get() + .asFile.absolutePath + + if (!config.debugApk.isPresent) { + // Don't set debug apk if not already set. #172 + project.log("Configuring fladle.debugApk from variant ${variant.name}") + config.debugApk.set(appApkPath) + } + if (!config.roboScript.isPresent && !config.instrumentationApk.isPresent && !config.sanityRobo.get()) { + // Don't set instrumentation apk if not already set. #172 + project.log("Configuring fladle.instrumentationApk from variant ${variant.name}") + config.instrumentationApk.set(testApkPath) + } } } } @@ -219,7 +302,7 @@ class FladlePluginDelegate { get() = configurations.getByName(FLADLE_CONFIG) companion object { - val GRADLE_MIN_VERSION: GradleVersion = GradleVersion.version("7.3") + val GRADLE_MIN_VERSION: GradleVersion = GradleVersion.version("9.1") const val TASK_GROUP = "fladle" const val FLADLE_CONFIG = "fladle" diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankExecutionTask.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankExecutionTask.kt index 168c5712..2518ff9d 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankExecutionTask.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankExecutionTask.kt @@ -8,7 +8,7 @@ import javax.inject.Inject @DisableCachingByDefault( because = "Flank executions are dependent on resources such as network connection and server and therefore cannot be cached.", ) -open class FlankExecutionTask +abstract class FlankExecutionTask @Inject constructor( projectLayout: ProjectLayout, @@ -22,7 +22,12 @@ open class FlankExecutionTask private fun checkFilesExist(base: FladleConfig) { if (base.serviceAccountCredentials.isPresent) { - check(base.serviceAccountCredentials.get().asFile.exists()) { + check( + base.serviceAccountCredentials + .get() + .asFile + .exists(), + ) { "serviceAccountCredential file doesn't exist ${base.serviceAccountCredentials.get()}" } } diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt index 19c0f938..25b496d1 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankGradleExtension.kt @@ -16,7 +16,9 @@ import javax.inject.Inject open class FlankGradleExtension @Inject - constructor(objects: ObjectFactory) : FladleConfig { + constructor( + objects: ObjectFactory, + ) : FladleConfig { companion object { const val FLANK_VERSION = "23.10.1" } diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankJavaExec.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankJavaExec.kt index f10ef850..21bb73aa 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankJavaExec.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FlankJavaExec.kt @@ -8,9 +8,11 @@ import javax.inject.Inject @DisableCachingByDefault( because = "Flank executions are dependent on resources such as network connection and server and therefore cannot be cached.", ) -open class FlankJavaExec +abstract class FlankJavaExec @Inject - constructor(projectLayout: ProjectLayout) : JavaExec() { + constructor( + projectLayout: ProjectLayout, + ) : JavaExec() { init { group = FladlePluginDelegate.TASK_GROUP mainClass.set("ftl.Main") diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladleModuleExtension.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladleModuleExtension.kt index 510f5f54..e6637bd1 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladleModuleExtension.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladleModuleExtension.kt @@ -9,7 +9,9 @@ import javax.inject.Inject open class FulladleModuleExtension @Inject - constructor(objects: ObjectFactory) { + constructor( + objects: ObjectFactory, + ) { /** * When set to false, Fulladle will not automatically add this module to additionalTestApks. * @@ -46,4 +48,10 @@ open class FulladleModuleExtension * can be a match. */ val variant: Property = objects.property().convention(null as String?) + + /** + * Variant APK info collected during configuration via onVariants callbacks. + * Used by FulladlePlugin at execution time to build YAML entries. + */ + internal val variantApks: MutableList = mutableListOf() } diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt index 568feebb..59be14be 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/FulladlePlugin.kt @@ -1,8 +1,12 @@ package com.osacky.flank.gradle -import com.android.build.gradle.TestedExtension +import com.android.build.api.variant.ApplicationAndroidComponentsExtension +import com.android.build.api.variant.FilterConfiguration +import com.android.build.api.variant.HasAndroidTest +import com.android.build.api.variant.LibraryAndroidComponentsExtension import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.plugins.BasePluginExtension import org.gradle.kotlin.dsl.getByType /** @@ -18,23 +22,107 @@ class FulladlePlugin : Plugin { root.subprojects { // Yuck, cross project configuration extensions.create("fulladleModuleConfig", FulladleModuleExtension::class.java) + + // Register onVariants callbacks to capture APK info during configuration + pluginManager.withPlugin("com.android.application") { + val androidComponents = extensions.getByType(ApplicationAndroidComponentsExtension::class.java) + val ext = extensions.findByType(FulladleModuleExtension::class.java) ?: return@withPlugin + androidComponents.onVariants { variant -> + val androidTest = (variant as? HasAndroidTest)?.androidTest ?: return@onVariants + val buildType = variant.buildType ?: return@onVariants + val flavorName = variant.productFlavors.joinToString("") { it.second } + val flavorPath = variant.productFlavors.joinToString("/") { it.second } + val archivesName = extensions.getByType(BasePluginExtension::class.java).archivesName.get() + + variant.outputs.forEach { output -> + val abiFilter = output.filters.firstOrNull { it.filterType == FilterConfiguration.FilterType.ABI } + val abiName = abiFilter?.identifier + + val appApkDirPath = if (flavorPath.isNotEmpty()) "$flavorPath/$buildType" else buildType + val appApkFileName = + buildString { + append(archivesName) + if (flavorName.isNotEmpty()) append("-$flavorName") + if (abiName != null) append("-$abiName") + append("-$buildType.apk") + } + val appApkPath = + layout.buildDirectory + .file("outputs/apk/$appApkDirPath/$appApkFileName") + .get() + .asFile.absolutePath + + val testApkDirPath = + if (flavorPath.isNotEmpty()) "androidTest/$flavorPath/$buildType" else "androidTest/$buildType" + val testApkFileName = + if (flavorName.isNotEmpty()) { + "$archivesName-$flavorName-$buildType-androidTest.apk" + } else { + "$archivesName-$buildType-androidTest.apk" + } + val testApkPath = + layout.buildDirectory + .file("outputs/apk/$testApkDirPath/$testApkFileName") + .get() + .asFile.absolutePath + + ext.variantApks.add( + VariantApkInfo( + variantName = variant.name, + appApkPath = appApkPath, + testApkPath = testApkPath, + abiName = abiName, + ), + ) + } + } + } + + pluginManager.withPlugin("com.android.library") { + val androidComponents = extensions.getByType(LibraryAndroidComponentsExtension::class.java) + val ext = extensions.findByType(FulladleModuleExtension::class.java) ?: return@withPlugin + androidComponents.onVariants { variant -> + val androidTest = (variant as? HasAndroidTest)?.androidTest ?: return@onVariants + val buildType = variant.buildType ?: return@onVariants + val flavorName = variant.productFlavors.joinToString("") { it.second } + val flavorPath = variant.productFlavors.joinToString("/") { it.second } + val archivesName = extensions.getByType(BasePluginExtension::class.java).archivesName.get() + + val testApkDirPath = + if (flavorPath.isNotEmpty()) "androidTest/$flavorPath/$buildType" else "androidTest/$buildType" + val testApkFileName = + if (flavorName.isNotEmpty()) { + "$archivesName-$flavorName-$buildType-androidTest.apk" + } else { + "$archivesName-$buildType-androidTest.apk" + } + val testApkPath = + layout.buildDirectory + .file("outputs/apk/$testApkDirPath/$testApkFileName") + .get() + .asFile.absolutePath + + ext.variantApks.add( + VariantApkInfo( + variantName = variant.name, + appApkPath = null, + testApkPath = testApkPath, + abiName = null, + ), + ) + } + } } val fulladleConfigureTask = root.tasks.register("configureFulladle") { var modulesEnabled = false - /** - * we will first configure all app modules - * then configure all library modules - * we force this order of configuration because - * app modules are better candidates to become - * root level test/app APKs, since they produce - * app APKs - * if no app module had tests or was enabled - * we will choose a library module to become - * a root level module, in which case we will - * have to check if it has its debugApk set - */ + // We first configure all app modules, then configure all library modules. + // We force this order because app modules are better candidates to become + // root level test/app APKs, since they produce app APKs. + // If no app module had tests or was enabled, we will choose a library module + // to become a root level module, in which case we will have to check if it + // has its debugApk set. doLast { // first configure all app modules root.subprojects { @@ -86,82 +174,80 @@ fun configureModule( return } - val testedExtension = extensions.findByType(TestedExtension::class.java) ?: return // Only configure the first test variant per module. // Does anyone test more than one variant per module? var addedTestsForModule = false - testedExtension.testVariants.configureEach testVariant@{ - if (this.isExpectedVariantInModule(fulladleModuleExtension)) { - testedVariant.outputs - .matching { it.isExpectedAbiOutput(flankGradleExtension) } - .configureEach app@{ - if (addedTestsForModule) { - return@app - } - this@testVariant.outputs.configureEach test@{ - val yml = StringBuilder() - // If the debugApk isn't yet set, let's use this one. - if (!flankGradleExtension.debugApk.isPresent) { - if (project.isAndroidAppModule) { - // app modules produce app apks that we can consume - flankGradleExtension.debugApk.set(rootProject.provider { this@app.outputFile.absolutePath }) - } else if (project.isAndroidLibraryModule) { - // library modules do not produce an app apk and we'll use the one specified in fulladleModuleConfig block - // we need library modules to specify the app apk to test against, even if it's a dummy one - check(fulladleModuleExtension.debugApk.isPresent && fulladleModuleExtension.debugApk.orNull != null) { - "Library module ${project.path} did not specify a debug apk. Library modules do not " + - "generate a debug apk and one needs to be specified in the fulladleModuleConfig block\n" + - "This is a required parameter in FTL which remains unused for library modules under test, " + - "and you can use a dummy apk here" - } - flankGradleExtension.debugApk.set(rootProject.provider { fulladleModuleExtension.debugApk.get() }) - } - } else { - // Otherwise, let's just add it to the list. - if (project.isAndroidAppModule) { - yml.appendLine("- app: ${this@app.outputFile}") - } else if (project.isAndroidLibraryModule) { - // app apk is not required for library modules so only use if it's explicitly specified - if (fulladleModuleExtension.debugApk.orNull != null) { - yml.appendLine("- app: ${fulladleModuleExtension.debugApk.get()}") - } - } - } + for (variantInfo in fulladleModuleExtension.variantApks) { + if (addedTestsForModule) break - // If the instrumentation apk isn't yet set, let's use this one. - if (!flankGradleExtension.instrumentationApk.isPresent) { - flankGradleExtension.instrumentationApk.set(rootProject.provider { this@test.outputFile.absolutePath }) - } else { - // Otherwise, let's just add it to the list. - if (yml.isBlank()) { - // The first item in the list needs to start with a ` - `. - yml.appendLine("- test: ${this@test.outputFile}") - } else { - yml.appendLine(" test: ${this@test.outputFile}") - } - } + if (!variantInfo.isExpectedVariantInModule(fulladleModuleExtension)) continue - if (yml.isEmpty()) { - // this is the root module - // should not be added as additional test apk - overrideRootLevelConfigs(flankGradleExtension, fulladleModuleExtension) - } else { - yml.appendProperty(fulladleModuleExtension.maxTestShards, " max-test-shards") - yml.appendMapProperty( - fulladleModuleExtension.clientDetails, - " client-details", - ) { appendLine(" ${it.key}: ${it.value}") } - yml.appendMapProperty( - fulladleModuleExtension.environmentVariables, - " environment-variables", - ) { appendLine(" ${it.key}: ${it.value}") } - flankGradleExtension.additionalTestApks.add(yml.toString()) - } - addedTestsForModule = true - } + // Check ABI filter against the extension + if (flankGradleExtension.abi.isPresent && variantInfo.abiName != null && variantInfo.abiName != flankGradleExtension.abi.get()) continue + if (flankGradleExtension.abi.isPresent && variantInfo.abiName == null) { + // No ABI filter on this output - it's a match (universal) + } + + val yml = StringBuilder() + // If the debugApk isn't yet set, let's use this one. + if (!flankGradleExtension.debugApk.isPresent) { + if (project.isAndroidAppModule && variantInfo.appApkPath != null) { + // app modules produce app apks that we can consume + flankGradleExtension.debugApk.set(rootProject.provider { variantInfo.appApkPath }) + } else if (project.isAndroidLibraryModule) { + // library modules do not produce an app apk and we'll use the one specified in fulladleModuleConfig block + // we need library modules to specify the app apk to test against, even if it's a dummy one + check(fulladleModuleExtension.debugApk.isPresent && fulladleModuleExtension.debugApk.orNull != null) { + "Library module ${project.path} did not specify a debug apk. Library modules do not " + + "generate a debug apk and one needs to be specified in the fulladleModuleConfig block\n" + + "This is a required parameter in FTL which remains unused for library modules under test, " + + "and you can use a dummy apk here" } + flankGradleExtension.debugApk.set(rootProject.provider { fulladleModuleExtension.debugApk.get() }) + } + } else { + // Otherwise, let's just add it to the list. + if (project.isAndroidAppModule && variantInfo.appApkPath != null) { + yml.appendLine("- app: ${variantInfo.appApkPath}") + } else if (project.isAndroidLibraryModule) { + // app apk is not required for library modules so only use if it's explicitly specified + if (fulladleModuleExtension.debugApk.orNull != null) { + yml.appendLine("- app: ${fulladleModuleExtension.debugApk.get()}") + } + } + } + + // If the instrumentation apk isn't yet set, let's use this one. + if (!flankGradleExtension.instrumentationApk.isPresent) { + flankGradleExtension.instrumentationApk.set(rootProject.provider { variantInfo.testApkPath }) + } else { + // Otherwise, let's just add it to the list. + if (yml.isBlank()) { + // The first item in the list needs to start with a ` - `. + yml.appendLine("- test: ${variantInfo.testApkPath}") + } else { + yml.appendLine(" test: ${variantInfo.testApkPath}") + } + } + + if (yml.isEmpty()) { + // this is the root module + // should not be added as additional test apk + overrideRootLevelConfigs(flankGradleExtension, fulladleModuleExtension) + } else { + yml.appendProperty(fulladleModuleExtension.maxTestShards, " max-test-shards") + yml.appendMapProperty( + fulladleModuleExtension.clientDetails, + " client-details", + ) { appendLine(" ${it.key}: ${it.value}") } + yml.appendMapProperty( + fulladleModuleExtension.environmentVariables, + " environment-variables", + ) { appendLine(" ${it.key}: ${it.value}") } + flankGradleExtension.additionalTestApks.add(yml.toString()) } + addedTestsForModule = true } } @@ -180,16 +266,11 @@ val Project.hasAndroidTest: Boolean if (!fulladleModuleExtension.enabled.get()) { return false } - val testedExtension = extensions.findByType(TestedExtension::class.java) ?: return false - var testsFound = true - testedExtension.testVariants.configureEach testVariant@{ - if (!file("$projectDir/src/androidTest").exists()) { - println("Ignoring $name test variant in $path: No tests in $projectDir/src/androidTest") - testsFound = false - } - return@testVariant + if (!file("$projectDir/src/androidTest").exists()) { + println("Ignoring test variants in $path: No tests in $projectDir/src/androidTest") + return false } - return testsFound + return true } fun overrideRootLevelConfigs( diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/RequiredDeviceKeyMissingException.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/RequiredDeviceKeyMissingException.kt index 31427c11..7682930f 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/RequiredDeviceKeyMissingException.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/RequiredDeviceKeyMissingException.kt @@ -1,3 +1,5 @@ package com.osacky.flank.gradle -data class RequiredDeviceKeyMissingException(val key: String) : Exception("Device should have '$key' key set to a value.") +data class RequiredDeviceKeyMissingException( + val key: String, +) : Exception("Device should have '$key' key set to a value.") diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/SanityConfigValidation.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/SanityConfigValidation.kt index 26d91f4a..b26bcd5f 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/SanityConfigValidation.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/SanityConfigValidation.kt @@ -6,16 +6,22 @@ import java.lang.IllegalArgumentException fun checkIfSanityAndValidateConfigs(config: FladleConfig) = when (config) { - is FlankGradleExtension -> + is FlankGradleExtension -> { config.checkAndValidateConfig { option, name -> "Incorrect [$name] configuration. [$option] can't be used together with sanityRobo." } - is FladleConfigImpl -> + } + + is FladleConfigImpl -> { config.checkAndValidateConfig(config.name) { option, name -> "Incorrect [$name] configuration. [$option] can't be used together with sanityRobo. " + "To configure sanityRobo, add clearPropertiesForSanityRobo() to the [$name] configuration" } - else -> throw IllegalArgumentException("Unexpected configuration when validating parameters. Did not expect: $config.") + } + + else -> { + throw IllegalArgumentException("Unexpected configuration when validating parameters. Did not expect: $config.") + } } private fun FladleConfig.checkAndValidateConfig( @@ -35,5 +41,5 @@ private fun FladleConfig.checkAndValidateConfig( val Property.hasValue get() = orNull.isNullOrBlank().not() -private val ListProperty.hasValue +private val ListProperty.hasValue get() = getOrElse(emptyList()).isNotEmpty() diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/VariantApkInfo.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/VariantApkInfo.kt new file mode 100644 index 00000000..b69db0fa --- /dev/null +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/VariantApkInfo.kt @@ -0,0 +1,11 @@ +package com.osacky.flank.gradle + +data class VariantApkInfo( + val variantName: String, + val appApkPath: String?, + val testApkPath: String, + val abiName: String?, +) { + fun isExpectedVariantInModule(config: FulladleModuleExtension): Boolean = + !config.variant.isPresent || (config.variant.isPresent && variantName.contains(config.variant.get())) +} diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/Variants.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/Variants.kt index 630ed2ab..df732443 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/Variants.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/Variants.kt @@ -1,33 +1,34 @@ package com.osacky.flank.gradle -import com.android.build.VariantOutput -import com.android.build.gradle.api.BaseVariant -import com.android.build.gradle.api.BaseVariantOutput +import com.android.build.api.variant.FilterConfiguration +import com.android.build.api.variant.Variant +import com.android.build.api.variant.VariantOutput /** - * Returns true if this [BaseVariant] matches the variant specified in the [config]. + * Returns true if this [Variant] matches the variant specified in the [config]. * * If no variant is specified, all variants are considered a match. */ -fun BaseVariant.isExpectedVariant(config: FladleConfig) = +fun Variant.isExpectedVariant(config: FladleConfig) = !config.variant.isPresent || (config.variant.isPresent && config.variant.get() == this.name) /** - * Returns true if this [BaseVariantOutput] matches the ABI specified in the [config]. + * Returns true if this [VariantOutput] matches the ABI specified in the [config]. * - * If the config does not specify an ABI, or if the config specifies an ABI but the [BaseVariantOutput] + * If the config does not specify an ABI, or if the config specifies an ABI but the [VariantOutput] * is not filtered by ABI, it is considered a match. */ -fun BaseVariantOutput.isExpectedAbiOutput(config: FladleConfig): Boolean { +fun VariantOutput.isExpectedAbiOutput(config: FladleConfig): Boolean { + val abiFilters = filters.filter { it.filterType == FilterConfiguration.FilterType.ABI } return !config.abi.isPresent || - !filterTypes.contains(VariantOutput.FilterType.ABI.name) || - filters.single { it.filterType == VariantOutput.FilterType.ABI.name }.identifier == config.abi.get() + abiFilters.isEmpty() || + abiFilters.any { it.identifier == config.abi.get() } } /** - * Returns true if this [BaseVariant] matches the variant specified in the [config]. + * Returns true if this [Variant] matches the variant specified in the [config]. * * If no variant is specified, all variants are considered a match. */ -fun BaseVariant.isExpectedVariantInModule(config: FulladleModuleExtension) = +fun Variant.isExpectedVariantInModule(config: FulladleModuleExtension) = !config.variant.isPresent || (config.variant.isPresent && this.name.contains(config.variant.get())) diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlConfigWriterTask.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlConfigWriterTask.kt index 12373cbb..1d3b09e6 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlConfigWriterTask.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlConfigWriterTask.kt @@ -38,21 +38,18 @@ open class YamlConfigWriterTask @get:Input val additionalTestApks: ListProperty = - objects.listProperty(String::class.java) + objects + .listProperty(String::class.java) .convention(config.additionalTestApks) @OutputFile val fladleConfigFile: Provider = fladleDir.map { it.file("flank.yml") } @Internal - override fun getDescription(): String { - return "Writes a flank.yml file based on the current FlankGradleExtension configuration." - } + override fun getDescription(): String = "Writes a flank.yml file based on the current FlankGradleExtension configuration." @Internal - override fun getGroup(): String { - return FladlePluginDelegate.TASK_GROUP - } + override fun getGroup(): String = FladlePluginDelegate.TASK_GROUP @TaskAction fun writeFile() { diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlExtensions.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlExtensions.kt index 75d41a73..a80543bc 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlExtensions.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlExtensions.kt @@ -4,14 +4,14 @@ import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property -fun StringBuilder.appendProperty( +fun StringBuilder.appendProperty( prop: Property, name: String, ) { if (prop.isPresent) appendLine(" $name: ${prop.get()}") } -fun StringBuilder.appendMapProperty( +fun StringBuilder.appendMapProperty( prop: MapProperty, name: String, custom: StringBuilder.(Map.Entry) -> Unit, @@ -22,7 +22,7 @@ fun StringBuilder.appendMapProperty( } } -fun StringBuilder.appendListProperty( +fun StringBuilder.appendListProperty( prop: ListProperty, name: String, custom: StringBuilder.(T) -> Unit, @@ -35,15 +35,16 @@ fun StringBuilder.appendListProperty( fun StringBuilder.appendAdditionalProperty(property: Property) { if (property.isPresent) { - property.get() + property + .get() .split("\n") .map { " $it" } .forEach { appendLine(it) } } } -val ListProperty.isPresentAndNotEmpty +val ListProperty.isPresentAndNotEmpty get() = isPresent && get().isNotEmpty() -val MapProperty.isPresentAndNotEmpty +val MapProperty.isPresentAndNotEmpty get() = isPresent && get().isNotEmpty() diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlWriter.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlWriter.kt index 2262148f..d2083f64 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlWriter.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/YamlWriter.kt @@ -126,7 +126,8 @@ internal class YamlWriter { } else { appendListProperty(config.roboDirectives, name = "robo-directives") { val value = - it.getOrElse(2) { "" } + it + .getOrElse(2) { "" } .let { stringValue -> if (stringValue.isBlank()) "\"\"" else stringValue } appendLine(" ${it[0]}:${it[1]}: $value") } diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/SinceFlank.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/SinceFlank.kt index 142b9713..f35e6222 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/SinceFlank.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/SinceFlank.kt @@ -1,3 +1,6 @@ package com.osacky.flank.gradle.validation -annotation class SinceFlank(val version: String, val hasDefaultValue: Boolean = false) +annotation class SinceFlank( + val version: String, + val hasDefaultValue: Boolean = false, +) diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/ValidateOptions.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/ValidateOptions.kt index 9fead49d..0a48b133 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/ValidateOptions.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/ValidateOptions.kt @@ -11,7 +11,8 @@ fun validateOptionsUsed( // if using snapshot version default to the latest known version of flank for validation checks val configFlankVersion = if (flank.toLowerCase().endsWith("snapshot")) FLANK_VERSION.toVersion() else flank.toVersion() - config.getPresentProperties() + config + .getPresentProperties() .mapNotNull { property -> properties[property.name]?.let { property to it } } .forEach { (property, version) -> if (version > configFlankVersion) { @@ -25,11 +26,18 @@ fun validateOptionsUsed( private fun String.toVersion() = VersionNumber.parse(this) private val properties = - FladleConfig::class.memberProperties + FladleConfig::class + .memberProperties .asSequence() .map { it to it.getter.annotations } // we also need to exclude properties with default values to preserve backward compatibility // to be fixed .filter { it.second.any { annotation -> annotation is SinceFlank && !annotation.hasDefaultValue } } - .map { it.first.name to it.second.filterIsInstance().first().version.toVersion() } - .toMap() + .map { + it.first.name to + it.second + .filterIsInstance() + .first() + .version + .toVersion() + }.toMap() diff --git a/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/VersionNumber.kt b/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/VersionNumber.kt index d17b4b3e..90d5d9cb 100644 --- a/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/VersionNumber.kt +++ b/fladle-plugin/src/main/java/com/osacky/flank/gradle/validation/VersionNumber.kt @@ -40,13 +40,13 @@ class VersionNumber private constructor( if (patch != other.patch) { return patch - other.patch } - return qualifier.orEmpty().lowercase() + return qualifier + .orEmpty() + .lowercase() .compareTo(other.qualifier.orEmpty().lowercase()) } - override fun equals(other: Any?): Boolean { - return other is VersionNumber && compareTo(other) == 0 - } + override fun equals(other: Any?): Boolean = other is VersionNumber && compareTo(other) == 0 override fun hashCode(): Int { var result = major @@ -57,9 +57,7 @@ class VersionNumber private constructor( return result } - override fun toString(): String { - return scheme.format(this) - } + override fun toString(): String = scheme.format(this) /** * Returns the version number scheme. @@ -70,7 +68,9 @@ class VersionNumber private constructor( fun format(versionNumber: VersionNumber): String } - private abstract class AbstractScheme protected constructor(val depth: Int) : Scheme { + private abstract class AbstractScheme protected constructor( + val depth: Int, + ) : Scheme { override fun parse(versionString: String): VersionNumber { if (versionString.isEmpty()) { return UNKNOWN @@ -109,16 +109,15 @@ class VersionNumber private constructor( return UNKNOWN } - private class Scanner(val str: String) { + private class Scanner( + val str: String, + ) { var pos: Int = 0 - fun hasDigit(): Boolean { - return pos < str.length && Character.isDigit(str.get(pos)) - } + fun hasDigit(): Boolean = pos < str.length && Character.isDigit(str.get(pos)) - fun isSeparatorAndDigit(vararg separators: Char): Boolean { - return pos < str.length - 1 && oneOf(*separators) && Character.isDigit(str.get(pos + 1)) - } + fun isSeparatorAndDigit(vararg separators: Char): Boolean = + pos < str.length - 1 && oneOf(*separators) && Character.isDigit(str.get(pos + 1)) fun oneOf(vararg separators: Char): Boolean { val current = str.get(pos) @@ -148,22 +147,19 @@ class VersionNumber private constructor( pos++ } - fun remainder(): String? { - return if (pos == str.length) null else str.substring(pos) - } + fun remainder(): String? = if (pos == str.length) null else str.substring(pos) } } private class DefaultScheme : AbstractScheme(3) { - override fun format(versionNumber: VersionNumber): String { - return String.format( + override fun format(versionNumber: VersionNumber): String = + String.format( VERSION_TEMPLATE, versionNumber.major, versionNumber.minor, versionNumber.micro, if (versionNumber.qualifier == null) "" else "-" + versionNumber.qualifier, ) - } companion object { private const val VERSION_TEMPLATE = "%d.%d.%d%s" @@ -178,8 +174,8 @@ class VersionNumber private constructor( fun version( major: Int, minor: Int = 0, - ): VersionNumber { - return VersionNumber( + ): VersionNumber = + VersionNumber( major = major, minor = minor, micro = 0, @@ -187,10 +183,7 @@ class VersionNumber private constructor( qualifier = null, scheme = DEFAULT_SCHEME, ) - } - fun parse(versionString: String): VersionNumber { - return DEFAULT_SCHEME.parse(versionString) - } + fun parse(versionString: String): VersionNumber = DEFAULT_SCHEME.parse(versionString) } } diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/MultipleConfigsTest.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/MultipleConfigsTest.kt index 8b7a732f..dcb28cd3 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/MultipleConfigsTest.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/MultipleConfigsTest.kt @@ -38,7 +38,8 @@ class MultipleConfigsTest { testProjectRoot.newFile("flank-gradle-service.json").writeText("{}") val result = - GradleRunner.create() + GradleRunner + .create() .withPluginClasspath() .withArguments("writeConfigPropsOrange", "--stacktrace") .forwardOutput() @@ -76,7 +77,8 @@ class MultipleConfigsTest { ) val regularConfig = - GradleRunner.create() + GradleRunner + .create() .withPluginClasspath() .withArguments("writeConfigProps") .forwardOutput() diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/AutoConfigureFladleTest.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/AutoConfigureFladleTest.kt index a4c870ae..848eb540 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/AutoConfigureFladleTest.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/AutoConfigureFladleTest.kt @@ -39,7 +39,8 @@ class AutoConfigureFladleTest { testProjectRoot.setupFixture(fixtureName) val result = - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .withArguments("assembleDebug", "assembleDebugAndroidTest", "printYml", "--stacktrace") diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/ConfigurationCacheTest.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/ConfigurationCacheTest.kt index 2f9b5582..83cbf547 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/ConfigurationCacheTest.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/ConfigurationCacheTest.kt @@ -115,7 +115,7 @@ class ConfigurationCacheTest { settings.writeText( """ plugins { - id 'com.gradle.enterprise' version '3.7' + id 'com.gradle.develocity' version '4.3' } """.trimIndent(), ) @@ -131,11 +131,11 @@ class ConfigurationCacheTest { assertThat(secondResult.output).contains("Reusing configuration cache.") } - private fun configCachingRunner(arg: String): GradleRunner { - return GradleRunner.create() + private fun configCachingRunner(arg: String): GradleRunner = + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .forwardOutput() .withArguments(arg, "--configuration-cache") - } } diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FlankGradlePluginIntegrationTest.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FlankGradlePluginIntegrationTest.kt index 1a1ff22d..d95df33f 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FlankGradlePluginIntegrationTest.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FlankGradlePluginIntegrationTest.kt @@ -11,8 +11,8 @@ class FlankGradlePluginIntegrationTest { @get:Rule var testProjectRoot = TemporaryFolder() - val minSupportGradleVersion = "7.3" - val oldVersion = "7.2" + val minSupportGradleVersion = "9.1.0" + val oldVersion = "9.0.0" fun writeBuildGradle(build: String) { testProjectRoot.writeBuildDotGradle(build) @@ -27,16 +27,17 @@ class FlankGradlePluginIntegrationTest { """.trimMargin(), ) val result = - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .withGradleVersion(oldVersion) .buildAndFail() - assertThat(result.output).contains("Fladle requires at minimum version Gradle 7.3. Detected version Gradle 7.2") + assertThat(result.output).contains("Fladle requires at minimum version Gradle 9.1. Detected version Gradle 9.0.0") } @Test - fun testGradleEightOh() { + fun testGradleNineOne() { writeBuildGradle( """plugins { | id "com.osacky.fladle" @@ -44,10 +45,11 @@ class FlankGradlePluginIntegrationTest { """.trimMargin(), ) val result = - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() - .withGradleVersion("8.0") + .withGradleVersion("9.1.0") .build() assertThat(result.output).contains("SUCCESS") @@ -61,7 +63,8 @@ class FlankGradlePluginIntegrationTest { |} """.trimMargin(), ) - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .withGradleVersion(minSupportGradleVersion) @@ -82,7 +85,8 @@ class FlankGradlePluginIntegrationTest { |} """.trimMargin(), ) - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .withGradleVersion(minSupportGradleVersion) @@ -103,7 +107,8 @@ class FlankGradlePluginIntegrationTest { """.trimMargin(), ) val result = - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .withGradleVersion(minSupportGradleVersion) @@ -131,7 +136,8 @@ class FlankGradlePluginIntegrationTest { ) testProjectRoot.newFile("foo").writeText("{}") val result = - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .withGradleVersion(minSupportGradleVersion) @@ -155,7 +161,8 @@ class FlankGradlePluginIntegrationTest { ) testProjectRoot.newFile("foo").writeText("{}") val result = - GradleRunner.create() + GradleRunner + .create() .withProjectDir(testProjectRoot.root) .withPluginClasspath() .withGradleVersion(minSupportGradleVersion) @@ -182,7 +189,8 @@ class FlankGradlePluginIntegrationTest { ) testProjectRoot.writeEmptyServiceCredential() val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withGradleVersion(minSupportGradleVersion) .withArguments("printYml") .buildAndFail() @@ -209,7 +217,8 @@ class FlankGradlePluginIntegrationTest { ) testProjectRoot.writeEmptyServiceCredential() val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withGradleVersion(minSupportGradleVersion) .withArguments("printYml") .buildAndFail() @@ -236,7 +245,8 @@ class FlankGradlePluginIntegrationTest { ) testProjectRoot.writeEmptyServiceCredential() val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withGradleVersion(minSupportGradleVersion) .withArguments("printYml") .buildAndFail() @@ -264,7 +274,8 @@ class FlankGradlePluginIntegrationTest { ) testProjectRoot.writeEmptyServiceCredential() val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withGradleVersion(minSupportGradleVersion) .withArguments("printYml") .buildAndFail() @@ -291,8 +302,9 @@ class FlankGradlePluginIntegrationTest { ) testProjectRoot.writeEmptyServiceCredential() val result = - testProjectRoot.gradleRunner() - .withGradleVersion("8.0") + testProjectRoot + .gradleRunner() + .withGradleVersion("9.1.0") .withArguments("printYmlFooConfig") .build() assertThat(result.task(":printYmlFooConfig")!!.outcome).isEqualTo(TaskOutcome.SUCCESS) diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt index 6706b2e2..7d2a3ecf 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/FulladlePluginIntegrationTest.kt @@ -10,7 +10,7 @@ class FulladlePluginIntegrationTest { @get:Rule var testProjectRoot = TemporaryFolder() - val agpDependency: String = "com.android.tools.build:gradle:4.2.1" + val agpDependency: String = "com.android.tools.build:gradle:9.0.1" fun writeBuildGradle(build: String) { val file = testProjectRoot.newFile("build.gradle") @@ -26,7 +26,8 @@ class FulladlePluginIntegrationTest { """.trimMargin(), ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments("help") .build() assertThat(result.output).contains("SUCCESS") @@ -88,7 +89,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() @@ -189,7 +191,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() @@ -271,7 +274,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() @@ -395,7 +399,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() @@ -446,10 +451,9 @@ class FulladlePluginIntegrationTest { testProjectRoot.newFile("settings.gradle").writeText( """ include '$appFixture' - include '$libraryFixture' include '$flavourProject' include '$flavourLibrary' - + dependencyResolutionManagement { repositories { mavenCentral() @@ -468,17 +472,17 @@ class FulladlePluginIntegrationTest { repositories { google() } - + dependencies { classpath '$agpDependency' } } - + plugins { id "com.osacky.fulladle" } - - + + fladle { serviceAccountCredentials = project.layout.projectDirectory.file("android-project/flank-gradle-5cf02dc90531.json") } @@ -486,7 +490,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() @@ -576,7 +581,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() assertThat(result.output).doesNotContain("max-test-shards: 4") @@ -632,7 +638,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .buildAndFail() @@ -708,7 +715,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .buildAndFail() @@ -797,7 +805,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() @@ -881,7 +890,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() @@ -967,7 +977,8 @@ class FulladlePluginIntegrationTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments(":printYml") .build() diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/TestFixtures.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/TestFixtures.kt index e9f83355..6ad7a89c 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/TestFixtures.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/TestFixtures.kt @@ -5,7 +5,11 @@ import org.junit.rules.TemporaryFolder import java.io.File fun TemporaryFolder.setupFixture(fixtureName: String) { - File(this::class.java.classLoader.getResource(fixtureName)!!.file).copyRecursively(newFile(fixtureName), true) + File( + this::class.java.classLoader + .getResource(fixtureName)!! + .file, + ).copyRecursively(newFile(fixtureName), true) } internal fun TemporaryFolder.writeBuildDotGradle(buildScript: String) = @@ -13,7 +17,8 @@ internal fun TemporaryFolder.writeBuildDotGradle(buildScript: String) = .writeText(buildScript) fun TemporaryFolder.gradleRunner() = - GradleRunner.create() + GradleRunner + .create() .withPluginClasspath() .forwardOutput() .withProjectDir(root) diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/VariantTests.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/VariantTests.kt index c0f9a0c5..29f40468 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/VariantTests.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/integration/VariantTests.kt @@ -67,15 +67,13 @@ class VariantTests { assertThat(result.output).doesNotContain(":assembleVanillaRelease") assertThat(result.output).doesNotContain(":assembleChocolate") - /** - * See #60 https://github.com/runningcode/fladle/issues/60 - testProjectRoot.writeEmptyServiceCredential() - val resultPrint = testProjectRoot.gradleRunner() - .withArguments("printYmlVanilla") - .build() - assertThat(resultPrint.output).contains("build/outputs/apk/vanilla/debug/chocovanilla-vanilla-debug.apk") - assertThat(resultPrint.output).contains("build/outputs/apk/androidTest/vanilla/debug/chocovanilla-vanilla-debug-androidTest.apk") - **/ + // See #60 https://github.com/runningcode/fladle/issues/60 + // testProjectRoot.writeEmptyServiceCredential() + // val resultPrint = testProjectRoot.gradleRunner() + // .withArguments("printYmlVanilla") + // .build() + // assertThat(resultPrint.output).contains("build/outputs/apk/vanilla/debug/chocovanilla-vanilla-debug.apk") + // assertThat(resultPrint.output).contains("build/outputs/apk/androidTest/vanilla/debug/chocovanilla-vanilla-debug-androidTest.apk") } @Test @@ -106,6 +104,8 @@ class VariantTests { |include ':android-project' """.trimMargin(), ) + testProjectRoot.newFile("local.properties").writeText("sdk.dir=${androidHome()}\n") + testProjectRoot.newFile("gradle.properties").writeText("android.useAndroidX=true") testProjectRoot.setupFixture("android-project") val flavors = if (withFlavors) { @@ -191,7 +191,8 @@ class VariantTests { if (dryRun) { arguments.add("--dry-run") } - return testProjectRoot.gradleRunner() + return testProjectRoot + .gradleRunner() .withArguments(arguments) .build() } diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateExclusionsTest.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateExclusionsTest.kt index 0b5d05b4..4ffb9517 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateExclusionsTest.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateExclusionsTest.kt @@ -32,7 +32,8 @@ class ValidateExclusionsTest { ) val result = - testProjectRoot.gradleRunner() + testProjectRoot + .gradleRunner() .withArguments("printYml") .buildAndFail() diff --git a/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateOptionsTest.kt b/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateOptionsTest.kt index 8ef828cd..0800d233 100644 --- a/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateOptionsTest.kt +++ b/fladle-plugin/src/test/java/com/osacky/flank/gradle/validation/ValidateOptionsTest.kt @@ -16,7 +16,12 @@ class ValidateOptionsTest { @get:Rule var testProjectRoot = TemporaryFolder() - private val objects = ProjectBuilder.builder().withName("project").build().objects + private val objects = + ProjectBuilder + .builder() + .withName("project") + .build() + .objects private lateinit var config: FladleConfig @Before diff --git a/gradle.properties b/gradle.properties index b2732dd2..b9267d76 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,8 +15,6 @@ org.gradle.jvmargs=-Xmx1536m # Android operating system, and which are packaged with your app's APK # https://developer.android.com/topic/libraries/support-library/androidx-rn android.useAndroidX=true -# Automatically convert third-party libraries to use AndroidX -android.enableJetifier=false kotlin.code.style=official diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 00000000..5b343e54 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/0b98aec810298c2c1d7fdac5dac37910/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/658299a896470fbb3103ba3a430ee227/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/536afcd1dff540251f85e5d2c80458cf/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/23adb857f3cb3cbe28750bc7faa7abc0/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/932015f6361ccaead0c6d9b8717ed96e/redirect +toolchainVendor=JETBRAINS +toolchainVersion=21 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6ccba2c1..1079e105 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,16 +1,16 @@ ## Generated by $ ./gradlew refreshVersionsCatalog [plugins] +foojay = { id = "org.gradle.toolchains.foojay-resolver-convention", version = "1.0.0"} ben-manes-versions = { id = "com.github.ben-manes.versions", version = "0.51.0" } -kotlinter = { id = "org.jmailen.kotlinter", version = "4.0.0" } +kotlinter = { id = "org.jmailen.kotlinter", version = "5.4.2" } gradle-plugin-publish = {id = "com.gradle.plugin-publish", version = "2.0.0" } vanniktech-publish = { id = "com.vanniktech.maven.publish", version = "0.35.0" } -kgp = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin"} agp = { id = "com.android.application", version.ref = "agp-version"} [versions] @@ -29,8 +29,7 @@ androidx-test-rules = "1.7.0" junit-version = "4.13.2" -kotlin = "2.2.21" -agp-version = "7.4.2" +agp-version = "9.0.1" flank-version = "23.10.1" [libraries] @@ -53,8 +52,6 @@ flank = { module = "com.github.flank:flank", version.ref = "flank-version" } junit = { group = "junit", name = "junit", version.ref = "junit-version" } -kotlin-stdlib-jdk7 = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib-jdk7", version.ref = "kotlin" } - gradle-enterprise = { module = "com.gradle:develocity-gradle-plugin", version = "3.19.2" } truth = "com.google.truth:truth:1.4.5" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d4081da4..2e111328 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/sample-android-library/build.gradle.kts b/sample-android-library/build.gradle.kts index 8cf64b3f..63a998cb 100644 --- a/sample-android-library/build.gradle.kts +++ b/sample-android-library/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("com.android.library") - kotlin("android") } fulladleModuleConfig { diff --git a/sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt b/sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt index ab896993..0b6fe002 100644 --- a/sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt +++ b/sample-android-library/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt @@ -12,7 +12,5 @@ class ExampleInstrumentedTest { } @Test - fun runAndFail() { - throw RuntimeException("Test failed") - } + fun runAndFail(): Unit = throw RuntimeException("Test failed") } diff --git a/sample-flavors-kotlin/build.gradle.kts b/sample-flavors-kotlin/build.gradle.kts index ff6a537a..2b131a90 100644 --- a/sample-flavors-kotlin/build.gradle.kts +++ b/sample-flavors-kotlin/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id ("com.android.application") - kotlin("android") id ("com.osacky.fladle") } diff --git a/sample-flavors-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt b/sample-flavors-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt index 5d25f62c..efa6aae8 100644 --- a/sample-flavors-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt +++ b/sample-flavors-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt @@ -23,7 +23,5 @@ class ExampleInstrumentedTest { } @Test - fun runAndFail() { - throw RuntimeException("Test failed") - } + fun runAndFail(): Unit = throw RuntimeException("Test failed") } diff --git a/sample-kotlin/build.gradle.kts b/sample-kotlin/build.gradle.kts index 4c8a977d..5d729cec 100644 --- a/sample-kotlin/build.gradle.kts +++ b/sample-kotlin/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id ("com.android.application") - kotlin("android") id ("com.osacky.fladle") } diff --git a/sample-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt b/sample-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt index 57d1a72f..cec17543 100644 --- a/sample-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt +++ b/sample-kotlin/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt @@ -22,7 +22,5 @@ class ExampleInstrumentedTest { } @Test - fun runAndFail() { - throw RuntimeException("Test failed") - } + fun runAndFail(): Unit = throw RuntimeException("Test failed") } diff --git a/sample/build.gradle b/sample/build.gradle index f39d1a7c..71b98804 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' apply plugin: 'com.osacky.fladle' android { diff --git a/sample/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt b/sample/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt index 2587b288..f71566f5 100644 --- a/sample/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt +++ b/sample/src/androidTest/java/com/osacky/flank/gradle/sample/ExampleInstrumentedTest.kt @@ -22,7 +22,5 @@ class ExampleInstrumentedTest { } @Test - fun runAndFail() { - throw RuntimeException("Test failed") - } + fun runAndFail(): Unit = throw RuntimeException("Test failed") } diff --git a/settings.gradle b/settings.gradle index bac20682..744f3555 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,6 +7,7 @@ pluginManagement { } plugins { + id "org.gradle.toolchains.foojay-resolver-convention" version "1.0.0" id "com.gradle.develocity" version "4.3" }