11package com.osacky.flank.gradle
22
3- import com.android.build.gradle.AppExtension
4- import com.android.build.gradle.TestedExtension
5- import com.android.build.gradle.internal.tasks.factory.dependsOn
6- import com.android.builder.model.TestOptions
3+ import com.android.build.api.dsl.ApplicationExtension
4+ import com.android.build.api.variant.ApplicationAndroidComponentsExtension
5+ import com.android.build.api.variant.FilterConfiguration
6+ import com.android.build.api.variant.HasAndroidTest
77import com.osacky.flank.gradle.validation.checkForExclusionUsage
88import com.osacky.flank.gradle.validation.validateOptionsUsed
99import org.gradle.api.GradleException
1010import org.gradle.api.Project
1111import org.gradle.api.artifacts.Configuration
12+ import org.gradle.api.plugins.BasePluginExtension
1213import org.gradle.api.tasks.TaskContainer
1314import org.gradle.kotlin.dsl.create
1415import org.gradle.util.GradleVersion
@@ -24,7 +25,10 @@ class FladlePluginDelegate {
2425
2526 target.tasks.register(" flankAuth" , FlankJavaExec ::class .java) {
2627 doFirst {
27- target.layout.fladleDir.get().asFile.mkdirs()
28+ target.layout.fladleDir
29+ .get()
30+ .asFile
31+ .mkdirs()
2832 }
2933 classpath = project.fladleConfig
3034 args = listOf (" auth" , " login" )
@@ -34,7 +38,6 @@ class FladlePluginDelegate {
3438 }
3539
3640 private fun checkMinimumGradleVersion () {
37- // Gradle 4.9 is required because we use the lazy task configuration API.
3841 if (GRADLE_MIN_VERSION > GradleVersion .current()) {
3942 throw GradleException (" Fladle requires at minimum version $GRADLE_MIN_VERSION . Detected version ${GradleVersion .current()} ." )
4043 }
@@ -44,24 +47,22 @@ class FladlePluginDelegate {
4447 project : Project ,
4548 base : FlankGradleExtension ,
4649 ) {
47- if (GradleVersion .current() > GradleVersion .version(" 6.1" )) {
48- base.flankVersion.finalizeValueOnRead()
49- base.flankCoordinates.finalizeValueOnRead()
50- base.serviceAccountCredentials.finalizeValueOnRead()
50+ base.flankVersion.finalizeValueOnRead()
51+ base.flankCoordinates.finalizeValueOnRead()
52+ base.serviceAccountCredentials.finalizeValueOnRead()
53+
54+ // Register onVariants callbacks before afterEvaluate for APK path detection
55+ project.pluginManager.withPlugin(" com.android.application" ) {
56+ if (! base.debugApk.isPresent || ! base.instrumentationApk.isPresent) {
57+ findDebugAndInstrumentationApk(project, base)
58+ }
5159 }
60+
5261 project.afterEvaluate {
5362 // Add Flank dependency to Fladle Configuration
5463 // Must be done afterEvaluate otherwise extension values will not be set.
5564 project.dependencies.add(FLADLE_CONFIG , " ${base.flankCoordinates.get()} :${base.flankVersion.get()} " )
5665
57- // Only use automatic apk path detection for 'com.android.application' projects.
58- project.pluginManager.withPlugin(" com.android.application" ) {
59- // 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
60- if (! base.debugApk.isPresent || ! base.instrumentationApk.isPresent) {
61- findDebugAndInstrumentationApk(project, base)
62- }
63- }
64-
6566 tasks.apply {
6667 createTasksForConfig(base, base, project, " " )
6768
@@ -95,22 +96,41 @@ class FladlePluginDelegate {
9596
9697 val writeConfigProps = register(" writeConfigProps$name " , YamlConfigWriterTask ::class .java, base, config, name)
9798
98- writeConfigProps.dependsOn(validateFladle)
99+ writeConfigProps.configure { dependsOn(validateFladle) }
99100
100101 register(" printYml$name " ) {
101102 description = " Print the flank.yml file to the console."
102103 group = TASK_GROUP
103104 dependsOn(writeConfigProps)
104105 doLast {
105- println (writeConfigProps.get().fladleConfigFile.get().asFile.readText())
106+ println (
107+ writeConfigProps
108+ .get()
109+ .fladleConfigFile
110+ .get()
111+ .asFile
112+ .readText(),
113+ )
106114 }
107115 }
108116
109117 register(" flankDoctor$name " , FlankJavaExec ::class .java) {
110118 if (useDefaultDir) setUpWorkingDir(configName)
111119 description = " Finds problems with the current configuration."
112120 classpath = project.fladleConfig
113- args = listOf (" firebase" , " test" , " android" , " doctor" , " -c" , writeConfigProps.get().fladleConfigFile.get().asFile.absolutePath)
121+ args =
122+ listOf (
123+ " firebase" ,
124+ " test" ,
125+ " android" ,
126+ " doctor" ,
127+ " -c" ,
128+ writeConfigProps
129+ .get()
130+ .fladleConfigFile
131+ .get()
132+ .asFile.absolutePath,
133+ )
114134 dependsOn(writeConfigProps)
115135 }
116136
@@ -122,31 +142,46 @@ class FladlePluginDelegate {
122142 args =
123143 if (project.hasProperty(" dumpShards" )) {
124144 listOf (
125- " firebase" , " test" , " android" , " run" , " -c" ,
126- writeConfigProps.get().fladleConfigFile.get().asFile.absolutePath, " --dump-shards" ,
145+ " firebase" ,
146+ " test" ,
147+ " android" ,
148+ " run" ,
149+ " -c" ,
150+ writeConfigProps
151+ .get()
152+ .fladleConfigFile
153+ .get()
154+ .asFile.absolutePath,
155+ " --dump-shards" ,
127156 )
128157 } else {
129158 listOf (
130- " firebase" , " test" , " android" , " run" , " -c" ,
131- writeConfigProps.get().fladleConfigFile.get().asFile.absolutePath,
159+ " firebase" ,
160+ " test" ,
161+ " android" ,
162+ " run" ,
163+ " -c" ,
164+ writeConfigProps
165+ .get()
166+ .fladleConfigFile
167+ .get()
168+ .asFile.absolutePath,
132169 )
133170 }
134171 if (config.serviceAccountCredentials.isPresent) {
135172 environment(mapOf (" GOOGLE_APPLICATION_CREDENTIALS" to config.serviceAccountCredentials.get()))
136173 }
137174 dependsOn(writeConfigProps)
138175 if (config.dependOnAssemble.isPresent && config.dependOnAssemble.get()) {
139- val testedExtension =
140- requireNotNull(project.extensions.findByType(TestedExtension ::class .java)) { " Could not find TestedExtension in ${project.name} " }
141- testedExtension.testVariants.configureEach {
142- if (testedVariant.isExpectedVariant(config)) {
143- if (testedVariant.assembleProvider.isPresent) {
144- dependsOn(testedVariant.assembleProvider)
145- }
146- if (assembleProvider.isPresent) {
147- dependsOn(assembleProvider)
148- }
149- }
176+ // Find assemble tasks by convention name pattern
177+ val variantName = config.variant.orNull
178+ if (variantName != null ) {
179+ val capitalizedVariant = variantName.capitalize()
180+ dependsOn(" assemble$capitalizedVariant " )
181+ dependsOn(" assemble${capitalizedVariant} AndroidTest" )
182+ } else {
183+ dependsOn(" assembleDebug" )
184+ dependsOn(" assembleDebugAndroidTest" )
150185 }
151186 }
152187 if (config.localResultsDir.hasValue) {
@@ -172,45 +207,93 @@ class FladlePluginDelegate {
172207 private fun automaticallyConfigureTestOrchestrator (
173208 project : Project ,
174209 config : FladleConfig ,
175- androidExtension : AppExtension ,
210+ androidExtension : ApplicationExtension ,
176211 ) {
177212 project.afterEvaluate {
213+ val execution = androidExtension.testOptions.execution.uppercase()
178214 val useOrchestrator =
179- androidExtension.testOptions.getExecutionEnum() == TestOptions . Execution . ANDROIDX_TEST_ORCHESTRATOR ||
180- androidExtension.testOptions.getExecutionEnum() == TestOptions . Execution . ANDROID_TEST_ORCHESTRATOR
215+ execution == " ANDROIDX_TEST_ORCHESTRATOR" ||
216+ execution == " ANDROID_TEST_ORCHESTRATOR"
181217 if (useOrchestrator) {
182218 log(" Automatically detected the use of Android Test Orchestrator" )
219+ config.useOrchestrator.set(true )
183220 }
184- config.useOrchestrator.set(useOrchestrator)
185221 }
186222 }
187223
188224 private fun findDebugAndInstrumentationApk (
189225 project : Project ,
190226 config : FladleConfig ,
191227 ) {
192- val baseExtension =
193- requireNotNull(project.extensions.findByType(AppExtension ::class .java)) { " Could not find AppExtension in ${project.name} " }
194- automaticallyConfigureTestOrchestrator(project, config, baseExtension)
195- baseExtension.testVariants.configureEach {
196- val appVariant = testedVariant
197- outputs.configureEach test@{
198- appVariant.outputs
199- .matching { it.isExpectedAbiOutput(config) }
200- .configureEach app@{
201- if (appVariant.isExpectedVariant(config)) {
202- if (! config.debugApk.isPresent) {
203- // Don't set debug apk if not already set. #172
204- project.log(" Configuring fladle.debugApk from variant ${this @app.name} " )
205- config.debugApk.set(this @app.outputFile.absolutePath)
206- }
207- if (! config.roboScript.isPresent && ! config.instrumentationApk.isPresent && ! config.sanityRobo.get()) {
208- // Don't set instrumentation apk if not already set. #172
209- project.log(" Configuring fladle.instrumentationApk from variant ${this @test.name} " )
210- config.instrumentationApk.set(this @test.outputFile.absolutePath)
211- }
212- }
228+ val androidExtension =
229+ requireNotNull(
230+ project.extensions.findByType(ApplicationExtension ::class .java),
231+ ) { " Could not find ApplicationExtension in ${project.name} " }
232+ automaticallyConfigureTestOrchestrator(project, config, androidExtension)
233+
234+ val androidComponents =
235+ requireNotNull(project.extensions.findByType(ApplicationAndroidComponentsExtension ::class .java)) {
236+ " Could not find ApplicationAndroidComponentsExtension in ${project.name} "
237+ }
238+
239+ androidComponents.onVariants { variant ->
240+ if (! variant.isExpectedVariant(config)) return @onVariants
241+ val androidTest = (variant as ? HasAndroidTest )?.androidTest ? : return @onVariants
242+
243+ val buildType = variant.buildType ? : return @onVariants
244+ val flavorName = variant.productFlavors.joinToString(" " ) { it.second }
245+ val flavorPath = variant.productFlavors.joinToString(" /" ) { it.second }
246+ val archivesName =
247+ project.extensions
248+ .getByType(BasePluginExtension ::class .java)
249+ .archivesName
250+ .get()
251+ val buildDir = project.layout.buildDirectory
252+
253+ // Test APK path
254+ val testApkDirPath = if (flavorPath.isNotEmpty()) " androidTest/$flavorPath /$buildType " else " androidTest/$buildType "
255+ val testApkFileName =
256+ if (flavorName.isNotEmpty()) {
257+ " $archivesName -$flavorName -$buildType -androidTest.apk"
258+ } else {
259+ " $archivesName -$buildType -androidTest.apk"
260+ }
261+ val testApkPath =
262+ buildDir
263+ .file(" outputs/apk/$testApkDirPath /$testApkFileName " )
264+ .get()
265+ .asFile.absolutePath
266+
267+ variant.outputs.forEach { output ->
268+ if (! output.isExpectedAbiOutput(config)) return @forEach
269+
270+ val abiFilter = output.filters.firstOrNull { it.filterType == FilterConfiguration .FilterType .ABI }
271+ val abiName = abiFilter?.identifier
272+
273+ val appApkDirPath = if (flavorPath.isNotEmpty()) " $flavorPath /$buildType " else buildType
274+ val appApkFileName =
275+ buildString {
276+ append(archivesName)
277+ if (flavorName.isNotEmpty()) append(" -$flavorName " )
278+ if (abiName != null ) append(" -$abiName " )
279+ append(" -$buildType .apk" )
213280 }
281+ val appApkPath =
282+ buildDir
283+ .file(" outputs/apk/$appApkDirPath /$appApkFileName " )
284+ .get()
285+ .asFile.absolutePath
286+
287+ if (! config.debugApk.isPresent) {
288+ // Don't set debug apk if not already set. #172
289+ project.log(" Configuring fladle.debugApk from variant ${variant.name} " )
290+ config.debugApk.set(appApkPath)
291+ }
292+ if (! config.roboScript.isPresent && ! config.instrumentationApk.isPresent && ! config.sanityRobo.get()) {
293+ // Don't set instrumentation apk if not already set. #172
294+ project.log(" Configuring fladle.instrumentationApk from variant ${variant.name} " )
295+ config.instrumentationApk.set(testApkPath)
296+ }
214297 }
215298 }
216299 }
@@ -219,7 +302,7 @@ class FladlePluginDelegate {
219302 get() = configurations.getByName(FLADLE_CONFIG )
220303
221304 companion object {
222- val GRADLE_MIN_VERSION : GradleVersion = GradleVersion .version(" 7.3 " )
305+ val GRADLE_MIN_VERSION : GradleVersion = GradleVersion .version(" 9.1 " )
223306 const val TASK_GROUP = " fladle"
224307 const val FLADLE_CONFIG = " fladle"
225308
0 commit comments