Skip to content

Commit

Permalink
Replace resource folder generation for instrumentation filters with n…
Browse files Browse the repository at this point in the history
…ew Variant API

Since the minimum AGP version bump, we can finally safely access the
source directories across all supported versions
  • Loading branch information
mannodermaus committed May 22, 2024
1 parent e396da1 commit 050ccce
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ import com.android.build.gradle.DynamicFeaturePlugin
import com.android.build.gradle.LibraryExtension
import com.android.build.gradle.LibraryPlugin
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.TestVariant
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.instrumentationTestVariant
import de.mannodermaus.gradle.plugins.junit5.internal.providers.DirectoryProvider
import de.mannodermaus.gradle.plugins.junit5.internal.providers.JavaDirectoryProvider
import de.mannodermaus.gradle.plugins.junit5.internal.providers.KotlinDirectoryProvider
Expand Down Expand Up @@ -68,11 +66,6 @@ private constructor(
?: emptySet()
}

fun instrumentationTestVariantOf(variant: Variant): TestVariant? {
return legacyVariants.firstOrNull { it.name == variant.name }
?.run { this.instrumentationTestVariant }
}

/* Private */

private fun directoryProvidersOf(legacyVariant: BaseVariant): Set<DirectoryProvider> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import de.mannodermaus.gradle.plugins.junit5.internal.config.PluginConfig
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.android
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.getAsList
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.getTaskName
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.hasDependency
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.instrumentationTestVariant
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.junit5Warn
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.namedOrNull
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.usesComposeIn
Expand All @@ -23,7 +23,6 @@ import de.mannodermaus.gradle.plugins.junit5.internal.utils.excludedPackagingOpt
import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5JacocoReport
import de.mannodermaus.gradle.plugins.junit5.tasks.AndroidJUnit5WriteFilters
import org.gradle.api.Project
import org.gradle.api.artifacts.Dependency
import org.gradle.api.tasks.testing.Test

internal fun configureJUnit5(
Expand Down Expand Up @@ -51,7 +50,7 @@ internal fun configureJUnit5(
variants.forEach { variant ->
configureUnitTests(it, variant)
configureJacoco(it, config, variant)
configureInstrumentationTests(it, config, variant)
configureInstrumentationTests(it, variant)
}
}
}
Expand Down Expand Up @@ -197,12 +196,11 @@ private fun AndroidJUnitPlatformExtension.configureJacoco(

private fun AndroidJUnitPlatformExtension.configureInstrumentationTests(
project: Project,
config: PluginConfig,
variant: Variant,
) {
if (!instrumentationTests.enabled.get()) return

config.instrumentationTestVariantOf(variant)?.let { instrumentationTestVariant ->
AndroidJUnit5WriteFilters.register(project, variant, instrumentationTestVariant)
variant.instrumentationTestVariant?.sources?.res?.let { sourceDirs ->
AndroidJUnit5WriteFilters.register(project, variant, sourceDirs)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package de.mannodermaus.gradle.plugins.junit5.internal.extensions

import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.api.TestVariant
import com.android.build.gradle.api.UnitTestVariant
import com.android.build.gradle.internal.api.TestedVariant

Expand All @@ -15,12 +14,3 @@ internal val BaseVariant.unitTestVariant: UnitTestVariant

return requireNotNull(this.unitTestVariant)
}

internal val BaseVariant.instrumentationTestVariant: TestVariant?
get() {
if (this !is TestedVariant) {
throw IllegalArgumentException("Argument is not TestedVariant: $this")
}

return this.testVariant
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.mannodermaus.gradle.plugins.junit5.internal.extensions

import com.android.build.api.variant.AndroidTest
import com.android.build.api.variant.HasAndroidTest
import com.android.build.api.variant.Variant

internal fun Variant.getTaskName(prefix: String = "", suffix: String = ""): String {
Expand All @@ -18,3 +20,6 @@ internal fun Variant.getTaskName(prefix: String = "", suffix: String = ""): Stri
append(suffix.capitalized())
}.toString()
}

internal val Variant.instrumentationTestVariant: AndroidTest?
get() = (this as? HasAndroidTest)?.androidTest
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
@file:Suppress("DEPRECATION")

package de.mannodermaus.gradle.plugins.junit5.tasks

import com.android.build.api.variant.SourceDirectories
import com.android.build.api.variant.Variant
import com.android.build.gradle.api.TestVariant
import de.mannodermaus.gradle.plugins.junit5.internal.config.INSTRUMENTATION_FILTER_RES_FILE_NAME
import de.mannodermaus.gradle.plugins.junit5.internal.config.JUnit5TaskConfig
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.getTaskName
import de.mannodermaus.gradle.plugins.junit5.internal.extensions.junitPlatform
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.tasks.CacheableTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputDirectory
Expand All @@ -25,20 +25,20 @@ import java.io.File
* This only allows tests to be filtered with @Tag annotations even in the instrumentation test realm.
* Other plugin DSL settings, like includeEngines/excludeEngines or includePattern/excludePattern
* are not copied out to file. This has to do with limitations of the backport implementation
* of the JUnit Platform Runner, as well as some incompatibilities between Gradle and Java with regards to
* how class name patterns are formatted.
* of the JUnit Platform Runner, as well as some incompatibilities between Gradle and Java
* regarding how class name patterns are formatted.
*/
@CacheableTask
public abstract class AndroidJUnit5WriteFilters : DefaultTask() {

internal companion object {
@Suppress("UnstableApiUsage")
fun register(
project: Project,
variant: Variant,
instrumentationTestVariant: TestVariant
sourceDirs: SourceDirectories.Layered,
): Boolean {
val outputFolder = File("${project.buildDir}/generated/res/android-junit5/${instrumentationTestVariant.name}")
val configAction = ConfigAction(project, variant, outputFolder)
val configAction = ConfigAction(project, variant)

val provider = project.tasks.register(
configAction.name,
Expand All @@ -48,67 +48,66 @@ public abstract class AndroidJUnit5WriteFilters : DefaultTask() {

// Connect the output folder of the task to the instrumentation tests
// so that they are bundled into the built test application
instrumentationTestVariant.registerGeneratedResFolders(
project.files(outputFolder).builtBy(provider)
sourceDirs.addGeneratedSourceDirectory(
taskProvider = provider,
wiredWith = AndroidJUnit5WriteFilters::outputFolder,
)
instrumentationTestVariant.mergeResourcesProvider.configure { it.dependsOn(provider) }

return true
}
}

@Input
public var includeTags: List<String> = emptyList()
@get:Input
public abstract val includeTags: ListProperty<String>

@Input
public var excludeTags: List<String> = emptyList()
@get:Input
public abstract val excludeTags: ListProperty<String>

@OutputDirectory
public var outputFolder: File? = null
@get:OutputDirectory
public abstract val outputFolder: DirectoryProperty

@TaskAction
public fun execute() {
this.outputFolder?.let { folder ->
// Clear out current contents of the generated folder
folder.deleteRecursively()

if (this.hasFilters()) {
folder.mkdirs()

// Re-write the new file structure into it;
// the generated file will have a fixed name & is located
// as a "raw" resource inside the output folder
val rawFolder = File(folder, "raw").apply { mkdirs() }
File(rawFolder, INSTRUMENTATION_FILTER_RES_FILE_NAME)
.bufferedWriter()
.use { writer ->
// This format is a nod towards the real JUnit 5 ConsoleLauncher's arguments
includeTags.forEach { tag -> writer.appendLine("-t $tag") }
excludeTags.forEach { tag -> writer.appendLine("-T $tag") }
}
}
// Clear out current contents of the generated folder
val folder = outputFolder.get().asFile
folder.deleteRecursively()

val includeTags = includeTags.get()
val excludeTags = excludeTags.get()

if (includeTags.isNotEmpty() || excludeTags.isNotEmpty()) {
folder.mkdirs()

// Re-write the new file structure into it;
// the generated file will have a fixed name & is located
// as a "raw" resource inside the output folder
val rawFolder = File(folder, "raw").apply { mkdirs() }
File(rawFolder, INSTRUMENTATION_FILTER_RES_FILE_NAME)
.bufferedWriter()
.use { writer ->
// This format is a nod towards the real JUnit 5 ConsoleLauncher's arguments
includeTags.forEach { tag -> writer.appendLine("-t $tag") }
excludeTags.forEach { tag -> writer.appendLine("-T $tag") }
}
}
}

private fun hasFilters() = includeTags.isNotEmpty() || excludeTags.isNotEmpty()

private class ConfigAction(
private val project: Project,
private val variant: Variant,
private val outputFolder: File
) {

val name: String = variant.getTaskName(prefix = "writeFilters", suffix = "androidTest")

val type = AndroidJUnit5WriteFilters::class.java

fun execute(task: AndroidJUnit5WriteFilters) {
task.outputFolder = outputFolder

// Access filters for this particular variant & provide them to the task, too
// Access filters for this particular variant & provide them to the task
val configuration = JUnit5TaskConfig(variant, project.junitPlatform)
task.includeTags = configuration.combinedIncludeTags.toList()
task.excludeTags = configuration.combinedExcludeTags.toList()
task.includeTags.set(configuration.combinedIncludeTags.toList())
task.excludeTags.set(configuration.combinedExcludeTags.toList())

// Output folder is applied by Android Gradle Plugin, so there is no reason to provide a value ourselves
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ interface AgpInstrumentationSupportTests : AgpVariantAwareTests {
val task = project.tasks.get<AndroidJUnit5WriteFilters>("writeFiltersDebugAndroidTest")
assertAll(
{ assertThat(task).isNotNull() },
{ assertThat(task.includeTags).containsExactly("global-include-tag") },
{ assertThat(task.excludeTags).containsExactly("debug-exclude-tag") }
{ assertThat(task.includeTags.get()).containsExactly("global-include-tag") },
{ assertThat(task.excludeTags.get()).containsExactly("debug-exclude-tag") }
)
},

Expand Down Expand Up @@ -99,15 +99,15 @@ interface AgpInstrumentationSupportTests : AgpVariantAwareTests {
dynamicTest("has a task for writing the freeDebug filters DSL to a resource file") {
val task = project.tasks.get<AndroidJUnit5WriteFilters>("writeFiltersFreeDebugAndroidTest")
assertThat(task).isNotNull()
assertThat(task.includeTags).containsExactly("global-include-tag", "freeDebug-include-tag")
assertThat(task.excludeTags).containsExactly("global-exclude-tag")
assertThat(task.includeTags.get()).containsExactly("global-include-tag", "freeDebug-include-tag")
assertThat(task.excludeTags.get()).containsExactly("global-exclude-tag")
},

dynamicTest("has a task for writing the paidDebug filters DSL to a resource file") {
val task = project.tasks.get<AndroidJUnit5WriteFilters>("writeFiltersPaidDebugAndroidTest")
assertThat(task).isNotNull()
assertThat(task.includeTags).containsExactly("global-include-tag")
assertThat(task.excludeTags).containsExactly("global-exclude-tag")
assertThat(task.includeTags.get()).containsExactly("global-include-tag")
assertThat(task.excludeTags.get()).containsExactly("global-exclude-tag")
},

dynamicTest("doesn't have tasks for writing the release filters DSL to a resource file") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@ class AndroidJUnit5WriteFiltersTests {
private fun Project.runTaskAndGetOutputFolder(): File {
val task = project.tasks.getByName("writeFiltersDebugAndroidTest") as AndroidJUnit5WriteFilters
task.execute()
return requireNotNull(task.outputFolder)
return requireNotNull(task.outputFolder.get().asFile)
}
}

0 comments on commit 050ccce

Please sign in to comment.