Set friend modules for android gradle test tasks

This fixes the following issues:
1. When product flavors were used, android unit tests could not refer
to internal symbols from main source set.
2. Android instrumentation tests could not refer
to internal symbols from main source set

    #KT-11166 fixed
This commit is contained in:
Alexey Tsvetkov
2016-06-14 08:39:39 +03:00
parent 0e428ca10c
commit af10c13ef1
15 changed files with 188 additions and 35 deletions
@@ -73,6 +73,7 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
var compilerCalled: Boolean = false
// TODO: consider more reliable approach (see usage)
var anyClassesCompiled: Boolean = false
var friendTaskName: String? = null
private val loggerInstance = Logging.getLogger(this.javaClass)
override fun getLogger() = loggerInstance
@@ -145,7 +146,6 @@ abstract class AbstractKotlinCompile<T : CommonCompilerArguments>() : AbstractCo
}
open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
override val compiler = K2JVMCompiler()
override fun createBlankArgs(): K2JVMCompilerArguments = K2JVMCompilerArguments()
@@ -154,12 +154,6 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
// Should be SourceDirectorySet or File
val srcDirsSources = HashSet<Any>()
private val testsMap = mapOf(
"compileTestKotlin" to "compileKotlin",
"compileDebugUnitTestKotlin" to "compileDebugKotlin",
"compileReleaseUnitTestKotlin" to "compileReleaseKotlin"
)
// TODO: find out whether we really need to be able to override destination dir here, and how it should work with destinationDir property
private val compilerDestinationDir: String get() = if (StringUtils.isEmpty(kotlinOptions.destination)) { kotlinDestinationDir?.path.orEmpty() } else { kotlinOptions.destination }
@@ -209,7 +203,7 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
args.moduleName = kotlinOptions.moduleName ?: extraProperties.getOrNull<String>("defaultModuleName")
args.languageVersion = kotlinOptions.languageVersion
fun addFriendPathForTestTask(taskName: String) {
fun addFriendPathForTestTask(taskName: String) {
logger.kotlinDebug("try to determine the output directory of corresponding $taskName task")
val tasks = project.getTasksByName("$taskName", false)
logger.kotlinDebug("tasks for $taskName: ${tasks}")
@@ -223,10 +217,8 @@ open class KotlinCompile() : AbstractKotlinCompile<K2JVMCompilerArguments>() {
}
}
testsMap.get(this.name)?.let {
addFriendPathForTestTask(it)
}
logger.kotlinDebug { "friendTaskName = $friendTaskName" }
friendTaskName?.let { addFriendPathForTestTask(it) }
logger.kotlinDebug("args.moduleName = ${args.moduleName}")
}
@@ -66,7 +66,6 @@
</dependency>
</dependencies>
<build>
<sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory>
@@ -48,7 +48,8 @@ abstract class KotlinSourceSetProcessor<T : AbstractCompile>(
val pluginName: String,
val compileTaskNameSuffix: String,
val taskDescription: String,
val compilerClass: Class<T>
val compilerClass: Class<T>,
val tasksProvider: KotlinTasksProvider
) {
abstract protected fun doTargetSpecificProcessing()
val logger = Logging.getLogger(this.javaClass)
@@ -98,9 +99,9 @@ abstract class KotlinSourceSetProcessor<T : AbstractCompile>(
open protected fun createKotlinCompileTask(suffix: String = ""): T {
val name = sourceSet.getCompileTaskName(compileTaskNameSuffix) + suffix
logger.kotlinDebug("Creating kotlin compile task $name with class $compilerClass")
val compile = project.tasks.create(name, compilerClass)
val compile = tasksProvider.createKotlinJVMTask(project, name)
compile.extensions.extraProperties.set("defaultModuleName", "${project.name}-$name")
return compile
return compilerClass.cast(compile)
}
open protected fun commonTaskConfiguration() {
@@ -116,13 +117,14 @@ class Kotlin2JvmSourceSetProcessor(
javaBasePlugin: JavaBasePlugin,
sourceSet: SourceSet,
val scriptHandler: ScriptHandler,
val tasksProvider: KotlinTasksProvider
tasksProvider: KotlinTasksProvider
) : KotlinSourceSetProcessor<AbstractCompile>(
project, javaBasePlugin, sourceSet,
pluginName = "kotlin",
compileTaskNameSuffix = "kotlin",
taskDescription = "Compiles the $sourceSet.kotlin.",
compilerClass = tasksProvider.kotlinJVMCompileTaskClass
compilerClass = tasksProvider.kotlinJVMCompileTaskClass,
tasksProvider = tasksProvider
) {
private companion object {
@@ -186,13 +188,14 @@ class Kotlin2JsSourceSetProcessor(
javaBasePlugin: JavaBasePlugin,
sourceSet: SourceSet,
val scriptHandler: ScriptHandler,
val tasksProvider: KotlinTasksProvider
tasksProvider: KotlinTasksProvider
) : KotlinSourceSetProcessor<AbstractCompile>(
project, javaBasePlugin, sourceSet,
pluginName = "kotlin2js",
taskDescription = "Compiles the kotlin sources in $sourceSet to JavaScript.",
compileTaskNameSuffix = "kotlin2Js",
compilerClass = tasksProvider.kotlinJSCompileTaskClass
compilerClass = tasksProvider.kotlinJSCompileTaskClass,
tasksProvider = tasksProvider
) {
val copyKotlinJsTaskName = sourceSet.getTaskName("copy", "kotlinJs")
@@ -5,6 +5,7 @@ import org.gradle.api.Project
import org.gradle.api.initialization.dsl.ScriptHandler
import org.gradle.api.logging.Logger
import org.gradle.api.logging.Logging
import org.jetbrains.kotlin.gradle.tasks.AndroidTasksProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinTasksProvider
// TODO: simplify: the complicated structure is a leftover from dynamic loading of plugin core, could be significantly simplified now
@@ -59,7 +60,7 @@ open class KotlinPluginWrapper: KotlinBasePluginWrapper() {
}
open class KotlinAndroidPluginWrapper : KotlinBasePluginWrapper() {
override fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler) = KotlinAndroidPlugin(scriptHandler, KotlinTasksProvider(pluginClassLoader))
override fun getPlugin(pluginClassLoader: ClassLoader, scriptHandler: ScriptHandler) = KotlinAndroidPlugin(scriptHandler, AndroidTasksProvider(pluginClassLoader))
}
open class Kotlin2JsPluginWrapper : KotlinBasePluginWrapper() {
@@ -0,0 +1,47 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Task
import org.jetbrains.annotations.TestOnly
abstract class TaskToFriendTaskMapper {
operator fun get(task: Task): String? =
getFriendByName(task.name)
@TestOnly
operator fun get(name: String): String? =
getFriendByName(name)
protected abstract fun getFriendByName(name: String): String?
}
sealed class RegexTaskToFriendTaskMapper(
private val prefix: String,
private val suffix: String
) : TaskToFriendTaskMapper() {
class Default : RegexTaskToFriendTaskMapper("compile", "TestKotlin")
class Android : RegexTaskToFriendTaskMapper("compile", "(Unit|Android)TestKotlin")
private val regex = "$prefix(.*)$suffix".toRegex()
override fun getFriendByName(name: String): String? {
val match = regex.matchEntire(name) ?: return null
val variant = match.groups[1]?.value ?: ""
return prefix + variant + "Kotlin"
}
}
@@ -53,6 +53,7 @@ internal fun AbstractCompile.appendClasspathDynamically(file: File) {
internal var AbstractTask.anyClassesCompiled: Boolean? by TaskPropertyDelegate("anyClassesCompiled")
internal var AbstractTask.kotlinDestinationDir: File? by TaskPropertyDelegate("kotlinDestinationDir")
internal var AbstractTask.friendTaskName: String? by TaskPropertyDelegate("friendTaskName")
inline
internal fun <reified T : Any> TaskPropertyDelegate(propertyName: String) =
@@ -4,6 +4,9 @@ import org.gradle.api.tasks.compile.AbstractCompile
import org.gradle.api.Task
import org.gradle.api.Project
import org.gradle.api.tasks.SourceTask
import org.jetbrains.kotlin.gradle.plugin.RegexTaskToFriendTaskMapper
import org.jetbrains.kotlin.gradle.plugin.TaskToFriendTaskMapper
import org.jetbrains.kotlin.gradle.plugin.friendTaskName
import java.net.URLClassLoader
/**
@@ -12,7 +15,7 @@ import java.net.URLClassLoader
* date: 17.12.2014.
*/
public open class KotlinTasksProvider(val tasksLoader: ClassLoader) {
open class KotlinTasksProvider(val tasksLoader: ClassLoader) {
val kotlinJVMCompileTaskClass: Class<AbstractCompile> =
tasksLoader.loadClass("org.jetbrains.kotlin.gradle.tasks.KotlinCompile") as Class<AbstractCompile>
@@ -22,12 +25,21 @@ public open class KotlinTasksProvider(val tasksLoader: ClassLoader) {
val kotlinJVMOptionsClass: Class<Any> =
tasksLoader.loadClass("org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments") as Class<Any>
public fun createKotlinJVMTask(project: Project, name: String): AbstractCompile {
return project.tasks.create(name, kotlinJVMCompileTaskClass)
fun createKotlinJVMTask(project: Project, name: String): AbstractCompile {
return project.tasks.create(name, kotlinJVMCompileTaskClass).apply {
friendTaskName = taskToFriendTaskMapper[this]
}
}
public fun createKotlinJSTask(project: Project, name: String): AbstractCompile {
fun createKotlinJSTask(project: Project, name: String): AbstractCompile {
return project.tasks.create(name, kotlinJSCompileTaskClass)
}
protected open val taskToFriendTaskMapper: TaskToFriendTaskMapper =
RegexTaskToFriendTaskMapper.Default()
}
class AndroidTasksProvider(tasksLoader: ClassLoader) : KotlinTasksProvider(tasksLoader) {
override val taskToFriendTaskMapper: TaskToFriendTaskMapper =
RegexTaskToFriendTaskMapper.Android()
}
@@ -18,11 +18,13 @@ abstract class AbstractKotlinAndroidGradleTests(
androidHome = File("../../../dependencies/android-sdk-for-tests"),
androidGradlePluginVersion = androidGradlePluginVersion)
@Test
fun testSimpleCompile() {
val project = Project("AndroidProject", gradleVersion)
project.build("build") {
project.build("build", "assembleAndroidTest") {
assertSuccessful()
assertContains(":Lib:compileReleaseKotlin",
":Test:compileDebugKotlin",
@@ -37,7 +39,10 @@ abstract class AbstractKotlinAndroidGradleTests(
":compileFlavor1Jnidebug",
":compileFlavor2Jnidebug",
":compileFlavor1Release",
":compileFlavor2Release")
":compileFlavor2Release",
":compileFlavor1DebugUnitTestKotlin",
"InternalDummyTest PASSED",
":compileFlavor1DebugAndroidTestKotlin")
}
// Run the build second time, assert everything is up-to-date
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle.plugin
import org.junit.Assert
import org.junit.Test
class RegexTaskToFriendTaskMapperTest {
@Test
fun getFriendTaskNameDefault() {
val mapper = RegexTaskToFriendTaskMapper.Default()
Assert.assertEquals("compileKotlin", mapper["compileTestKotlin"])
Assert.assertEquals(null, mapper["compileKotlin"])
}
@Test
fun getFriendTaskNameAndroid() {
val mapper = RegexTaskToFriendTaskMapper.Android()
// Unit test examples
Assert.assertEquals("compileDebugKotlin", mapper["compileDebugUnitTestKotlin"])
Assert.assertEquals("compileReleaseKotlin", mapper["compileReleaseUnitTestKotlin"])
Assert.assertEquals("compileProdDebugKotlin", mapper["compileProdDebugUnitTestKotlin"])
// Android test examples
Assert.assertEquals("compileDebugKotlin", mapper["compileDebugAndroidTestKotlin"])
Assert.assertEquals("compileReleaseKotlin", mapper["compileReleaseAndroidTestKotlin"])
Assert.assertEquals("compileProdDebugKotlin", mapper["compileProdDebugAndroidTestKotlin"])
Assert.assertEquals(null, mapper["compileDebugKotlin"])
}
}
@@ -4,6 +4,8 @@ apply plugin: 'kotlin-android'
dependencies {
compile project(':Lib')
compile "org.jetbrains.kotlin:kotlin-stdlib:1.1-SNAPSHOT"
testCompile 'junit:junit:4.12'
}
android {
@@ -12,6 +14,7 @@ android {
sourceSets {
main.kotlin.srcDirs += 'root/kotlin'
test.kotlin.srcDirs += 'src/test/kotlin'
}
defaultConfig {
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.gradle
import android.app.Application
import android.test.ApplicationTestCase
class InternalDummyApplicationTest : ApplicationTestCase<Application>(Application::class.java) {
init {
val dummy = InternalDummy("World")
assert("Hello World!" == dummy.greeting) { "Expected: 'Hello World!'. Actual value: ${dummy.greeting}" }
}
}
@@ -0,0 +1,6 @@
package org.jetbrains.kotlin.gradle
internal class InternalDummy(private val name: String) {
internal val greeting: String
get() = "Hello $name!"
}
@@ -0,0 +1,12 @@
package org.jetbrains.kotlin.gradle
import org.junit.Assert
import org.junit.Test
class InternalDummyTest {
@Test
fun testInternalDummy() {
val dummy = InternalDummy("World")
Assert.assertEquals("Hello World!", dummy.greeting)
}
}
@@ -2,10 +2,6 @@ apply plugin: 'android-sdk-manager'
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
repositories {
mavenCentral()
}
dependencies {
compile files('libs/android-support-v4.jar')
}
@@ -1,8 +1,6 @@
buildscript {
repositories {
maven {
url 'file://' + pathToKotlinPlugin
}
maven { url 'file://' + pathToKotlinPlugin }
maven {
url 'http://jcenter.bintray.com'
}
@@ -14,4 +12,11 @@ buildscript {
classpath ('com.android.tools.build:gradle:' + androidToolsVersion)
classpath 'com.jakewharton.sdkmanager:gradle-plugin:0.12.+'
}
}
allprojects {
repositories {
maven { url 'file://' + pathToKotlinPlugin }
mavenCentral()
}
}