Skip to content

Commit 6e3df98

Browse files
authored
Replace usage of Scope API for variants (#209)
* Add AGP 4.1 line to the list of tested plugin versions * Replace Scope API with manual task name composition AGP 4.1 removes the Scope API, which necessitates this change * Reduce visibility of plugin's internal APIs Make them actually `internal` * Changelog
1 parent ef75d00 commit 6e3df98

23 files changed

Lines changed: 197 additions & 95 deletions

File tree

buildSrc/src/main/kotlin/Artifacts.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ object Artifacts {
3535
platform = Java,
3636
groupId = "de.mannodermaus.gradle.plugins",
3737
artifactId = "android-junit5",
38-
currentVersion = "1.6.1.0-SNAPSHOT",
38+
currentVersion = "1.6.0.1-SNAPSHOT",
3939
latestStableVersion = "1.6.0.0",
4040
license = license,
4141
description = "Unit Testing with JUnit 5 for Android."

buildSrc/src/main/kotlin/Libs.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ object Libs {
4040
Versions.com_android_tools_build_gradle_36x
4141
const val com_android_tools_build_gradle_40x: String = "com.android.tools.build:gradle:" +
4242
Versions.com_android_tools_build_gradle_40x
43+
const val com_android_tools_build_gradle_41x: String = "com.android.tools.build:gradle:" +
44+
Versions.com_android_tools_build_gradle_41x
4345

4446
/**
4547
* https://developer.android.com/studio */

buildSrc/src/main/kotlin/Versions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ object Versions {
1818
const val com_android_tools_build_gradle_35x: String = "3.5.0"
1919
const val com_android_tools_build_gradle_36x: String = "3.6.0"
2020
const val com_android_tools_build_gradle_40x: String = "4.0.0-beta01"
21+
const val com_android_tools_build_gradle_41x: String = "4.1.0-alpha01"
2122
const val com_android_tools_build_gradle: String = com_android_tools_build_gradle_35x
2223

2324
const val lint_gradle: String = "26.5.0"

plugin/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ Change Log
44
## Unreleased
55

66
#### Added
7+
- Compatibility with Android Gradle Plugin 4.1.x
78
#### Changed
9+
- Restricted visibility of internal APIs to prevent them from leaking into consumer code
810
#### Fixed
911
#### Removed
1012

plugin/android-junit5/build.gradle.kts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ gradlePlugin {
5050
// Use JUnit 5
5151
tasks.withType<Test> {
5252
useJUnitPlatform()
53+
failFast = true
5354
testLogging {
5455
events = setOf(TestLogEvent.STARTED, TestLogEvent.SKIPPED, TestLogEvent.FAILED)
5556
exceptionFormat = TestExceptionFormat.FULL
@@ -67,6 +68,7 @@ tasks.named("processTestResources", Copy::class.java).configure {
6768
"AGP_35X" to Versions.com_android_tools_build_gradle_35x,
6869
"AGP_36X" to Versions.com_android_tools_build_gradle_36x,
6970
"AGP_40X" to Versions.com_android_tools_build_gradle_40x,
71+
"AGP_41X" to Versions.com_android_tools_build_gradle_41x,
7072
"KOTLIN" to Versions.org_jetbrains_kotlin,
7173

7274
"KOTLIN_STD_LIB" to Libs.kotlin_stdlib,
@@ -112,7 +114,8 @@ data class AgpConfiguration(val version: String, val dependency: String) {
112114
private val agpConfigurations = listOf(
113115
AgpConfiguration("3.5", Libs.com_android_tools_build_gradle_35x),
114116
AgpConfiguration("3.6", Libs.com_android_tools_build_gradle_36x),
115-
AgpConfiguration("4.0", Libs.com_android_tools_build_gradle_40x)
117+
AgpConfiguration("4.0", Libs.com_android_tools_build_gradle_40x),
118+
AgpConfiguration("4.1", Libs.com_android_tools_build_gradle_41x)
116119
)
117120

118121
configurations {

plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/Interop.kt

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package de.mannodermaus.gradle.plugins.junit5
22

33
import com.android.build.gradle.api.BaseVariant
4-
import com.android.build.gradle.internal.variant.BaseVariantData
54
import de.mannodermaus.gradle.plugins.junit5.internal.invokeTyped
65
import de.mannodermaus.gradle.plugins.junit5.internal.reflectiveMethod
76
import org.gradle.api.Project
@@ -23,17 +22,6 @@ import java.io.File
2322
* =====================================================================================================================
2423
*/
2524

26-
/**
27-
* Obtains the VariantData of the provided Variant.
28-
*
29-
* @because 'BaseVariant.variantData' is protected and can't be accessed through Kotlin directly
30-
* @return The variant data
31-
*/
32-
val BaseVariant.variantData: BaseVariantData
33-
get() = this.reflectiveMethod("getVariantData")
34-
?.invokeTyped<BaseVariantData>(this)
35-
?: throw IllegalArgumentException("BaseVariant.variantData not available")
36-
3725
/**
3826
* Obtains the Java class directory for the provided variant in a safe manner.
3927
*

plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/ConfigurationKind.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package de.mannodermaus.gradle.plugins.junit5.internal
22

3-
enum class ConfigurationKind(internal val value: String) {
3+
internal enum class ConfigurationKind(internal val value: String) {
44
APP(""),
55
TEST("test"),
66
ANDROID_TEST("androidTest")

plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/ConfigurationScope.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package de.mannodermaus.gradle.plugins.junit5.internal
22

3-
enum class ConfigurationScope(internal vararg val values: String) {
3+
internal enum class ConfigurationScope(internal vararg val values: String) {
44
API("api", "compile"),
55
IMPLEMENTATION("implementation", "compile"),
66
COMPILE_ONLY("compileOnly", "provided"),

plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/Extensions.kt

Lines changed: 41 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,10 @@ import com.github.zafarkhaja.semver.Version
1010
import de.mannodermaus.gradle.plugins.junit5.AndroidJUnitPlatformPlugin
1111
import de.mannodermaus.gradle.plugins.junit5.JUnit5TaskConfig
1212
import de.mannodermaus.gradle.plugins.junit5.VariantTypeCompat
13-
import de.mannodermaus.gradle.plugins.junit5.internal.ConfigurationKind.APP
14-
import de.mannodermaus.gradle.plugins.junit5.variantData
1513
import org.gradle.api.GradleException
1614
import org.gradle.api.Project
1715
import org.gradle.api.Task
18-
import org.gradle.api.artifacts.Configuration
1916
import org.gradle.api.artifacts.dsl.DependencyHandler
20-
import org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestFramework
2117
import org.gradle.api.logging.LogLevel
2218
import org.gradle.api.logging.LogLevel.ERROR
2319
import org.gradle.api.logging.LogLevel.INFO
@@ -26,8 +22,6 @@ import org.gradle.api.logging.Logger
2622
import org.gradle.api.plugins.ExtensionAware
2723
import org.gradle.api.plugins.ExtraPropertiesExtension
2824
import org.gradle.api.tasks.TaskContainer
29-
import org.gradle.api.tasks.testing.Test
30-
import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions
3125
import org.gradle.util.GradleVersion
3226
import java.util.Properties
3327

@@ -45,7 +39,7 @@ internal fun requireGradle(version: String, message: () -> String) {
4539
}
4640
}
4741

48-
fun requireVersion(actual: String, required: String, message: () -> String) {
42+
internal fun requireVersion(actual: String, required: String, message: () -> String) {
4943
val actualVersion = Version.valueOf(actual)
5044
val requiredVersion = Version.valueOf(required)
5145
require(actualVersion.greaterThanOrEqualTo(requiredVersion)) {
@@ -65,7 +59,7 @@ internal fun loadProperties(resource: String): Properties {
6559
* and splits it into a list using the delimiter.
6660
* Returns an empty List if the key doesn't exist in the Map.
6761
*/
68-
fun Map<String, String>.getAsList(key: String, delimiter: String = ","): List<String> =
62+
internal fun Map<String, String>.getAsList(key: String, delimiter: String = ","): List<String> =
6963
this[key]?.split(delimiter) ?: emptyList()
7064

7165
/**
@@ -74,7 +68,7 @@ fun Map<String, String>.getAsList(key: String, delimiter: String = ","): List<St
7468
* the value is appended to the end of the current value
7569
* using the given delimiter.
7670
*/
77-
fun MutableMap<String, String>.append(
71+
internal fun MutableMap<String, String>.append(
7872
key: String, value: String, delimiter: String = ","): String? {
7973
val insertedValue = if (containsKey(key)) {
8074
"${this[key]}$delimiter$value"
@@ -91,7 +85,7 @@ fun MutableMap<String, String>.append(
9185
* Log a message with the Android Gradle Plugin style syntax,
9286
* which will cause Android Studio to pick it up & display it inside the Messages window.
9387
*/
94-
fun Logger.agpLog(level: LogLevel, message: String) {
88+
internal fun Logger.agpLog(level: LogLevel, message: String) {
9589
val pair: Pair<String, (String) -> Unit> = when (level) {
9690
ERROR -> "error" to { s -> error(s) }
9791
WARN -> "warning" to { s -> warn(s) }
@@ -107,7 +101,7 @@ fun Logger.agpLog(level: LogLevel, message: String) {
107101
/**
108102
* Create & add an Extension to the given container by name.
109103
*/
110-
inline fun <reified T> Any.extend(
104+
internal inline fun <reified T> Any.extend(
111105
name: String,
112106
args: Array<Any> = emptyArray(),
113107
noinline init: ((T) -> Unit)? = null): T {
@@ -119,11 +113,11 @@ inline fun <reified T> Any.extend(
119113

120114
// Create & Configure the new extension
121115
val created: T = this.extensions.create(name, T::class.java, *args)
122-
init?.let { init(created) }
116+
init?.let { it(created) }
123117
return created
124118
}
125119

126-
fun Any.extensionExists(name: String): Boolean {
120+
internal fun Any.extensionExists(name: String): Boolean {
127121
if (this !is ExtensionAware) {
128122
throw IllegalArgumentException("Argument is not ExtensionAware: $this")
129123
}
@@ -135,7 +129,7 @@ fun Any.extensionExists(name: String): Boolean {
135129
* Obtain an Extension by name & directly cast it to the expected type.
136130
*/
137131
@Suppress("UNCHECKED_CAST")
138-
fun <T> Any.extensionByName(name: String): T {
132+
internal fun <T> Any.extensionByName(name: String): T {
139133
if (this !is ExtensionAware) {
140134
throw IllegalArgumentException("Argument is not ExtensionAware: $this")
141135
}
@@ -146,27 +140,27 @@ fun <T> Any.extensionByName(name: String): T {
146140
/**
147141
* Log the provided info message using the plugin's Log Tag.
148142
*/
149-
fun Logger.junit5Info(text: String) {
143+
internal fun Logger.junit5Info(text: String) {
150144
info("[android-junit5]: $text")
151145
}
152146

153147
/**
154148
* Log the provided warning message using the plugin's Log Tag.
155149
*/
156-
fun Logger.junit5Warn(text: String) {
150+
internal fun Logger.junit5Warn(text: String) {
157151
warn("[android-junit5]: $text")
158152
}
159153

160154
/**
161155
* Shorthand function to check for the existence of a plugin on a Project.
162156
*/
163-
fun Project.hasPlugin(name: String) = this.plugins.findPlugin(name) != null
157+
internal fun Project.hasPlugin(name: String) = this.plugins.findPlugin(name) != null
164158

165159
/**
166160
* Access the Android extension applied by a respective plugin.
167161
* Equivalent to "Project#android" in Groovy.
168162
*/
169-
val Project.android: BaseExtension
163+
internal val Project.android: BaseExtension
170164
get() = this.extensions.getByName("android") as BaseExtension
171165

172166
internal fun Project.junit5ConfigurationOf(variant: BaseVariant) =
@@ -176,14 +170,14 @@ internal fun Project.junit5ConfigurationOf(variant: BaseVariant) =
176170
* Access the extra properties of a DependencyHandler.
177171
* Equivalent to "DependencyHandler#ext" in Groovy.
178172
*/
179-
val DependencyHandler.ext: ExtraPropertiesExtension
173+
internal val DependencyHandler.ext: ExtraPropertiesExtension
180174
get() {
181175
val aware = this as ExtensionAware
182176
return aware.extensions.getByName(
183177
ExtraPropertiesExtension.EXTENSION_NAME) as ExtraPropertiesExtension
184178
}
185179

186-
val BaseVariant.unitTestVariant: UnitTestVariant
180+
internal val BaseVariant.unitTestVariant: UnitTestVariant
187181
get() {
188182
if (this !is TestedVariant) {
189183
throw IllegalArgumentException("Argument is not TestedVariant: $this")
@@ -192,7 +186,7 @@ val BaseVariant.unitTestVariant: UnitTestVariant
192186
return this.unitTestVariant
193187
}
194188

195-
val BaseVariant.instrumentationTestVariant: TestVariant?
189+
internal val BaseVariant.instrumentationTestVariant: TestVariant?
196190
get() {
197191
if (this !is TestedVariant) {
198192
throw IllegalArgumentException("Argument is not TestedVariant: $this")
@@ -201,12 +195,34 @@ val BaseVariant.instrumentationTestVariant: TestVariant?
201195
return this.testVariant
202196
}
203197

198+
/**
199+
* Composes the name of the variant-specific Gradle task with the given [prefix] and [suffix].
200+
* For example, to obtain the "free" variant's assemble task name, call `variant.getTaskName(prefix = "assemble")`.
201+
*/
202+
internal fun BaseVariant.getTaskName(prefix: String = "", suffix: String = ""): String {
203+
// At least one value must be provided
204+
require(prefix.isNotEmpty() || suffix.isNotEmpty())
205+
206+
return StringBuilder().apply {
207+
append(prefix)
208+
append(if (isEmpty()) {
209+
name
210+
} else {
211+
name.capitalize()
212+
})
213+
append(suffix.capitalize())
214+
}.toString()
215+
}
216+
204217
/**
205218
* Obtains the {AndroidUnitTest} for the provided variant.
206219
*/
207-
fun TaskContainer.testTaskOf(variant: BaseVariant): AndroidUnitTest {
208-
val taskName = variant.variantData.scope.getTaskName(VariantTypeCompat.UNIT_TEST_PREFIX,
209-
VariantTypeCompat.UNIT_TEST_SUFFIX)
220+
internal fun TaskContainer.testTaskOf(variant: BaseVariant): AndroidUnitTest {
221+
// From AGP 4.1 onwards, there is no Scope API on VariantData anymore.
222+
// Task names must be constructed manually
223+
val taskName = variant.getTaskName(
224+
prefix = VariantTypeCompat.UNIT_TEST_PREFIX,
225+
suffix = VariantTypeCompat.UNIT_TEST_SUFFIX)
210226
return getByName(taskName) as AndroidUnitTest
211227
}
212228

@@ -216,7 +232,7 @@ fun TaskContainer.testTaskOf(variant: BaseVariant): AndroidUnitTest {
216232
* in which case the existing task is returned.
217233
*/
218234
@Suppress("UNCHECKED_CAST")
219-
fun TaskContainer.maybeCreate(name: String, group: String? = null): Task {
235+
internal fun TaskContainer.maybeCreate(name: String, group: String? = null): Task {
220236
val existing = findByName(name)
221237
return if (existing != null) {
222238
existing

plugin/android-junit5/src/main/kotlin/de/mannodermaus/gradle/plugins/junit5/internal/Reflective.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import java.lang.reflect.Method
1313
* @param paramTypes Parameter types of the method's signature
1414
* @return A reference to the method with that signature, or null if it doesn't exist
1515
*/
16-
fun Any.reflectiveMethod(named: String, vararg paramTypes: Class<*>): Method? =
16+
internal fun Any.reflectiveMethod(named: String, vararg paramTypes: Class<*>): Method? =
1717
try {
1818
val targetClass = if (this is Class<*>) {
1919
this
@@ -34,6 +34,6 @@ fun Any.reflectiveMethod(named: String, vararg paramTypes: Class<*>): Method? =
3434
* @param params Parameters to call the method with
3535
* @return Result, cast to the desired type
3636
*/
37-
fun <T> Method.invokeTyped(target: Any? = null, vararg params: Any?): T? {
37+
internal fun <T> Method.invokeTyped(target: Any? = null, vararg params: Any?): T? {
3838
return this.invoke(target, *params) as? T?
3939
}

0 commit comments

Comments
 (0)