Skip to content

Commit adea3d6

Browse files
author
Marcel Schnelle
committed
Additional tweaks to JUnit 5 task's classpath for both AGP 2.x & 3.x
1 parent 5072e44 commit adea3d6

File tree

7 files changed

+191
-236
lines changed

7 files changed

+191
-236
lines changed

android-junit5/build.gradle

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ dependencies {
4545
compile localGroovy()
4646
compile "com.github.zafarkhaja:java-semver:$SEMVER_VERSION"
4747
compile "org.junit.platform:junit-platform-gradle-plugin:$JUNIT_PLATFORM_VERSION"
48-
compileOnly "com.android.tools.build:gradle:$ANDROID_PLUGIN_VERSION_2X"
48+
compileOnly "com.android.tools.build:gradle:$ANDROID_PLUGIN_VERSION_3X"
4949

5050
testCompile "junit:junit:$JUNIT4_VERSION"
5151
testCompile("org.spockframework:spock-core:$SPOCK_VERSION") { transitive = false }
52-
testCompileOnly "com.android.tools.build:gradle:$ANDROID_PLUGIN_VERSION_3X"
5352

5453
testAgp2xCompile "com.android.tools.build:gradle:$ANDROID_PLUGIN_VERSION_2X"
5554
testAgp3xCompile "com.android.tools.build:gradle:$ANDROID_PLUGIN_VERSION_3X"
@@ -68,7 +67,10 @@ task testAgp3x(type: Test) {
6867
}
6968

7069
// Combine all tests when executing the main JUnit task
71-
tasks.getByName("test").dependsOn(testAgp2x, testAgp3x)
70+
tasks.getByName("test").dependsOn(
71+
testAgp2x,
72+
testAgp3x
73+
)
7274

7375
version = VERSION_NAME
7476

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,53 @@
11
package de.mannodermaus.gradle.anj5
22

3+
import com.android.build.gradle.internal.publishing.AndroidArtifacts
34
import com.android.build.gradle.internal.scope.VariantScope
45
import com.github.zafarkhaja.semver.Version
6+
import com.google.common.collect.ImmutableList
7+
import com.google.common.collect.Iterables
8+
import org.gradle.api.Project
59

610
class AndroidJUnit5Compat {
711

12+
private static final int AGP_2 = 2
813
private static final Version AGP_2_2_0 = Version.valueOf("2.2.0-alpha1")
9-
private static final Version AGP_3_0_0 = Version.valueOf("3.0.0-alpha1")
1014

1115
/**
1216
* Fetches the Java output directories for the given Variant scopes
1317
* across different versions of the Android Gradle plugin.
18+
* @param project Gradle project context
1419
* @param agpVersion Version of the Android Gradle Plugin
1520
* @param variantScope VariantScope to look up the Java outputs from
1621
* @see {@link VariantScope}
1722
* @return An Iterable container depicting the output directories
1823
*/
19-
static Iterable<File> getJavaOutputDirs(Version agpVersion, def variantScope) {
20-
if (agpVersion.lessThan(AGP_2_2_0)) {
21-
// Below the first alpha of AGP 2.2.0, there was a typo in VariantScope
22-
// related to the Java outputs of a Variant
23-
return variantScope.javaOuptuts
24+
@SuppressWarnings("GroovyAssignabilityCheck")
25+
static Iterable<File> getJavaOutputDirs(Project project, Version agpVersion, def variantScope) {
26+
if (agpVersion.majorVersion <= AGP_2) {
27+
// AGP 2.x: Java Outputs & Annotation Processor outputs are aggregated.
28+
// Wee need to account for a typo that existed before 2.2.0
29+
def javaOutputs = agpVersion.lessThan(AGP_2_2_0) ?
30+
variantScope.javaOuptuts :
31+
variantScope.javaOutputs
2432

25-
} else if (agpVersion.lessThan(AGP_3_0_0)) {
26-
// Below the first alpha of AGP 3.0.0, use the unified VariantScope method
27-
return variantScope.javaOutputs
33+
// Add the runtime configuration explicitly
34+
def testApk = project.configurations.findByName("testApk")
35+
36+
return Iterables.concat(
37+
javaOutputs,
38+
testApk != null ?
39+
ImmutableList.of(variantScope.annotationProcessorOutputDir, testApk) :
40+
ImmutableList.of(variantScope.annotationProcessorOutputDir))
2841

2942
} else {
30-
// On and after AGP 3.0.0, use the separated methods for Java and annotation processors
31-
return [
32-
variantScope.javaOutputDir as File,
33-
variantScope.annotationProcessorOutputDir as File
34-
]
43+
// AGP 3.x: Use the refined Java Classpath API to collect runtime elements.
44+
// Since we're querying the entire runtime classpath directly,
45+
// there's no need to add the runtime configuration ("testRuntimeOnly") explicitly
46+
return Iterables.concat(
47+
variantScope.getJavaClasspath(
48+
AndroidArtifacts.ConsumedConfigType.RUNTIME_CLASSPATH,
49+
AndroidArtifacts.ArtifactType.CLASSES),
50+
ImmutableList.of(variantScope.javaOutputDir))
3551
}
3652
}
3753
}

android-junit5/src/main/groovy/de/mannodermaus/gradle/anj5/AndroidJUnitPlatformPlugin.groovy

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.mannodermaus.gradle.anj5
22

3+
import com.android.SdkConstants
34
import com.android.build.gradle.internal.scope.VariantScope
45
import com.github.zafarkhaja.semver.Version
56
import org.gradle.api.GradleException
@@ -22,7 +23,8 @@ class AndroidJUnitPlatformPlugin extends JUnitPlatformPlugin {
2223

2324
private static final String LOG_TAG = "[android-junit5]"
2425

25-
private static final String VINTAGE_WARNING = "AGPBI: {\"kind\":\"warning\",\"text\":\"$LOG_TAG You don't need to depend on junitVintage() directly anymore!\",\"sources\":[{},{}]}"
26+
private static
27+
final String VINTAGE_WARNING = "AGPBI: {\"kind\":\"warning\",\"text\":\"$LOG_TAG You don't need to depend on junitVintage() directly anymore!\",\"sources\":[{},{}]}"
2628

2729
private static final String EXTENSION_NAME = 'junitPlatform'
2830
private static final String TASK_NAME = 'junitPlatformTest'
@@ -113,13 +115,13 @@ class AndroidJUnitPlatformPlugin extends JUnitPlatformPlugin {
113115
// Obtain variant properties
114116
def variantData = variant.variantData
115117
VariantScope variantScope = variantData.scope
116-
def scopeJavaOutputs = AndroidJUnit5Compat.getJavaOutputDirs(agpVersion, variantScope)
118+
def scopeJavaOutputs = AndroidJUnit5Compat.getJavaOutputDirs(project, agpVersion, variantScope)
117119

118120
// Obtain tested variant properties
119121
def testedVariantData = variant.testedVariant.variantData
120122
VariantScope testedVariantScope = testedVariantData.scope
121123

122-
def testedScopeJavaOutputs = AndroidJUnit5Compat.getJavaOutputDirs(agpVersion, testedVariantScope)
124+
def testedScopeJavaOutputs = AndroidJUnit5Compat.getJavaOutputDirs(project, agpVersion, testedVariantScope)
123125

124126
// Collect the root directories for unit tests from the variant's scopes
125127
def testRootDirs = []
@@ -134,19 +136,10 @@ class AndroidJUnitPlatformPlugin extends JUnitPlatformPlugin {
134136
def javaCompiler = variant.javaCompiler
135137
classpath.add(javaCompiler.classpath)
136138
classpath.add(javaCompiler.outputs.files)
137-
} else {
138-
classpath.add(testedScopeJavaOutputs)
139-
classpath.add(scopeJavaOutputs)
140139
}
141140

142-
// 2) Add the runtime configurations
143-
def testRuntime = project.configurations.findByName("testRuntimeOnly")
144-
if (testRuntime == null) {
145-
testRuntime = project.configurations.findByName("testApk")
146-
}
147-
if (testRuntime != null) {
148-
classpath.add(testRuntime)
149-
}
141+
classpath.add(testedScopeJavaOutputs)
142+
classpath.add(scopeJavaOutputs)
150143

151144
// 3) Add test resources
152145
classpath.add(variantData.javaResourcesForUnitTesting)
@@ -155,7 +148,7 @@ class AndroidJUnitPlatformPlugin extends JUnitPlatformPlugin {
155148
// 4) Add filtered boot classpath
156149
def globalScope = variantScope.globalScope
157150
classpath.add(globalScope.androidBuilder.getBootClasspath(false).findAll {
158-
it.name != "android.jar"
151+
it.name != SdkConstants.FN_FRAMEWORK_LIBRARY
159152
})
160153

161154
// 5) Add mocked version of android.jar

android-junit5/src/test/groovy/de/mannodermaus/gradle/anj5/AndroidJUnitPlatformSpec.groovy

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ abstract class AndroidJUnitPlatformSpec extends Specification {
5757
testRoot.file("local.properties").withWriter { it.write(sdkDir) }
5858
}
5959

60+
/* Abstract */
61+
62+
protected abstract String testCompileDependency()
63+
protected abstract String testRuntimeDependency()
64+
65+
6066
/* Test Cases */
6167

6268
def "requires android plugin"() {
@@ -119,6 +125,85 @@ abstract class AndroidJUnitPlatformSpec extends Specification {
119125
p.tasks.getByName("junitPlatformTestRelease")
120126
}
121127

128+
def "classpath assembled correctly"() {
129+
when:
130+
// Prepare another test project to link to
131+
Project testApkProject = ProjectBuilder.builder()
132+
.withParent(testRoot)
133+
.withName("library")
134+
.build()
135+
testApkProject.file(".").mkdir()
136+
testApkProject.file("src/main").mkdirs()
137+
testApkProject.file("src/main/AndroidManifest.xml").withWriter { it.write(ANDROID_MANIFEST) }
138+
139+
testApkProject.apply plugin: 'com.android.library'
140+
testApkProject.android {
141+
compileSdkVersion COMPILE_SDK
142+
buildToolsVersion BUILD_TOOLS
143+
}
144+
145+
Project p = ProjectBuilder.builder().withParent(testRoot).build()
146+
p.file(".").mkdir()
147+
p.file("src/main").mkdirs()
148+
p.file("src/main/AndroidManifest.xml").withWriter { it.write(ANDROID_MANIFEST) }
149+
150+
p.apply plugin: 'com.android.application'
151+
p.apply plugin: 'de.mannodermaus.android-junit5'
152+
p.repositories {
153+
jcenter()
154+
}
155+
p.android {
156+
compileSdkVersion COMPILE_SDK
157+
buildToolsVersion BUILD_TOOLS
158+
159+
defaultConfig {
160+
applicationId APPLICATION_ID
161+
minSdkVersion MIN_SDK
162+
targetSdkVersion TARGET_SDK
163+
versionCode VERSION_CODE
164+
versionName VERSION_NAME
165+
}
166+
}
167+
p.dependencies {
168+
// "testApk" or "testRuntimeOnly"
169+
add(testRuntimeDependency(), testApkProject)
170+
}
171+
p.evaluate()
172+
173+
then:
174+
// Check that all expected folders are contained in the JUnit task's classpath
175+
[p.tasks.getByName("junitPlatformTestDebug"),
176+
p.tasks.getByName("junitPlatformTestRelease")].forEach { task ->
177+
def classpath = task.classpath.collect { it.absolutePath }
178+
179+
// println "---- $task.name"
180+
// classpath.each {
181+
// println " > $it"
182+
// }
183+
184+
// Source set's outputs
185+
assert classpath.find { it.contains("test/build/intermediates/classes/") } != null
186+
assert classpath.find { it.contains("test/build/intermediates/classes/test/") } != null
187+
188+
// Annotation Processor outputs
189+
assert classpath.find { it.contains("test/build/generated/source/apt/") } != null
190+
assert classpath.find { it.contains("test/build/generated/source/apt/test/") } != null
191+
192+
// Resource Files
193+
assert classpath.find { it.contains("test/build/intermediates/sourceFolderJavaResources/") } != null
194+
assert classpath.find { it.contains("test/build/intermediates/sourceFolderJavaResources/test/") } != null
195+
196+
// Mockable android.jar
197+
assert classpath.find { it.contains("build/generated/mockable-android-") } != null
198+
199+
// Runtime Library dependency (each version stores it slightly differently)
200+
assert classpath.find {
201+
it.contains("library/build/outputs/") ||
202+
it.contains("library/build/intermediates/")
203+
} != null
204+
}
205+
}
206+
122207
def "application with product flavors"() {
123208
when:
124209
Project p = ProjectBuilder.builder().withParent(testRoot).build()
@@ -160,4 +245,43 @@ abstract class AndroidJUnitPlatformSpec extends Specification {
160245
p.tasks.getByName("junitPlatformTestPaidDebug")
161246
p.tasks.getByName("junitPlatformTestPaidRelease")
162247
}
248+
249+
def "show warning if depending on junitVintage() directly"() {
250+
when:
251+
Project p = ProjectBuilder.builder().withParent(testRoot).build()
252+
253+
p.file(".").mkdir()
254+
p.file("src/main").mkdirs()
255+
p.file("src/main/AndroidManifest.xml").withWriter { it.write(ANDROID_MANIFEST) }
256+
257+
p.apply plugin: 'com.android.application'
258+
p.apply plugin: 'de.mannodermaus.android-junit5'
259+
p.android {
260+
compileSdkVersion COMPILE_SDK
261+
buildToolsVersion BUILD_TOOLS
262+
263+
defaultConfig {
264+
applicationId APPLICATION_ID
265+
minSdkVersion MIN_SDK
266+
targetSdkVersion TARGET_SDK
267+
versionCode VERSION_CODE
268+
versionName VERSION_NAME
269+
}
270+
}
271+
p.repositories {
272+
jcenter()
273+
}
274+
p.dependencies {
275+
// "testCompile" or "testApi"
276+
invokeMethod(testCompileDependency(), junitJupiter())
277+
// "testApk" or "testRuntimeOnly"
278+
invokeMethod(testRuntimeDependency(), junitVintage())
279+
}
280+
281+
then:
282+
p.evaluate()
283+
// Unsure how to capture the output directly
284+
// (Project.logging listeners don't seem to work)
285+
assert true == true
286+
}
163287
}

0 commit comments

Comments
 (0)