Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ package com.dropbox.affectedmoduledetector

import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.tasks.testing.Test
import java.util.*

/**
* This plugin creates and registers all affected test tasks.
Expand All @@ -22,19 +22,17 @@ import java.util.*
* configure using affected module detector block after applying the plugin
*
* affectedModuleDetector {
* baseDir = "${project.rootDir}"
* pathsAffectingAllModules = [
* "buildSrc/"
* ]
* logFolder = "${project.rootDir}".
* baseDir = "${project.rootDir}"
* pathsAffectingAllModules = [
* "buildSrc/"
* ]
* logFolder = "${project.rootDir}".
* }
*
*
* To enable affected module detection, you need to pass [ENABLE_ARG] into the build as a command line parameter
* See [AffectedModuleDetector] for additonal flags
*/
//TODO MIKE: add logging for why we filtered out
enum class TestType { JVM, ASSEMBLE_ANDROID, RUN_ANDROID }
class AffectedModuleDetectorPlugin : Plugin<Project> {

override fun apply(project: Project) {
Expand All @@ -56,69 +54,76 @@ class AffectedModuleDetectorPlugin : Plugin<Project> {
}

private fun registerJVMTests(project: Project) {
val rootProject = project.rootProject
registerAffectedTestTask("runAffectedUnitTests", TestType.JVM, rootProject)
registerAffectedTestTask(TestType.JvmTest("runAffectedUnitTests"), project)
}

private fun registerAffectedConnectedTestTask(rootProject: Project) {
registerAffectedTestTask("runAffectedAndroidTests", TestType.RUN_ANDROID, rootProject)
registerAffectedTestTask(TestType.RunAndroidTest("runAffectedAndroidTests"), rootProject)
}

private fun registerAffectedAndroidTests(rootProject: Project) {
registerAffectedTestTask("assembleAffectedAndroidTests", TestType.ASSEMBLE_ANDROID, rootProject)
registerAffectedTestTask(TestType.AssembleAndroidTest("assembleAffectedAndroidTests"), rootProject)
}

private fun registerAffectedTestTask(
taskName: String, testType: TestType,
testType: TestType,
rootProject: Project
) {
val task = rootProject.tasks.register(taskName).get()
val task = rootProject.tasks.register(testType.name).get()
rootProject.subprojects { project ->
project.afterEvaluate {
val paths = getAffectedPaths(testType, project)
paths.forEach { path ->
task.dependsOn(path)
val pluginIds = listOf("com.android.application", "com.android.library", "java-library")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kotlin library?

pluginIds.forEach { pluginId ->
if (pluginId == "java-library") {
if (testType is TestType.JvmTest ) {
withPlugin(pluginId, task, testType, project)
}
} else {
withPlugin(pluginId, task, testType, project)
}
}
task.enabled = paths.isNotEmpty()
task.onlyIf { paths.isNotEmpty() }
}
}
}

private fun getAffectedPaths(
private fun withPlugin(pluginId: String, task: Task, testType: TestType, project: Project) {
project.pluginManager.withPlugin(pluginId) {
val path = getAffectedPath(testType, project)
path?.let {
task.dependsOn(it)
}
}
}

private fun getAffectedPath(
testType: TestType,
project: Project
): Set<String> {
val paths = LinkedHashSet<String>()

): String? {
val tasks = requireNotNull(
project.extensions.findByName(AffectedTestConfiguration.name)
) as AffectedTestConfiguration

var pathName = ""
var backupPath: String? = null

when (testType) {
TestType.JVM -> {
pathName = "${project.path}:${tasks.jvmTest}"
backupPath = "${project.path}:${tasks.jvmTestBackup}"
}
TestType.RUN_ANDROID -> pathName = "${project.path}:${tasks.runAndroidTestTask}"
TestType.ASSEMBLE_ANDROID -> pathName =
"${project.path}:${tasks.assembleAndroidTestTask}"
) {
"Unable to find ${AffectedTestConfiguration.name} in $project"
} as AffectedTestConfiguration

val pathName = when (testType) {
is TestType.RunAndroidTest -> getPathAndTask(project, tasks.runAndroidTestTask)
is TestType.AssembleAndroidTest -> getPathAndTask(project, tasks.assembleAndroidTestTask)
is TestType.JvmTest -> getPathAndTask(project, tasks.jvmTestTask)
}

if (AffectedModuleDetector.isProjectAffected(project)) {
if (project.tasks.findByPath(pathName) != null) {
paths.add(pathName)
} else if (backupPath != null &&
project.tasks.findByPath(backupPath) != null
) {
paths.add(backupPath)
}
return if (AffectedModuleDetector.isProjectAffected(project)) {
pathName
} else {
null
}
}

return paths
private fun getPathAndTask(project: Project, task: String?): String? {
return if (task.isNullOrEmpty()) {
null
} else {
"${project.path}:${task}"
}
}

private fun filterAndroidTests(project: Project) {
Expand Down Expand Up @@ -155,5 +160,9 @@ class AffectedModuleDetectorPlugin : Plugin<Project> {
}
}


private sealed class TestType(open val name: String) {
data class RunAndroidTest(override val name: String) : TestType(name)
class AssembleAndroidTest(override val name: String) : TestType(name)
class JvmTest(override val name: String) : TestType(name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,14 @@ package com.dropbox.affectedmoduledetector
/**
* Used to configure which variant to run for affected tasks by adding following block to modules
* affectedTestConfiguration{
* variantToTest = "debug"
* jvmTestBackup = "test"
* assembleAndroidTestTask = "assembleDevDebugAndroidTest"
* }
*/
open class AffectedTestConfiguration {

/**
* Sets variant for all affected tasks
* By default `run` tasks will use debug as the variant to test ie
* gradlew runAffectedUnitTests will run testDebugUnitTest
*
*/
var variantToTest: String? = null
get() {
return field ?: "debug"
}

/**
* when [jvmTest] task is not found we will try to run [jvmTestBackup]
* this is normally used for modules that are not android variant aware
*/
var jvmTestBackup = "test"

val assembleAndroidTestTask get() = "assemble${variantToTest?.capitalize()}AndroidTest"
val runAndroidTestTask get() = "connected${variantToTest?.capitalize()}AndroidTest"
val jvmTest get() = "test${variantToTest?.capitalize()}UnitTest"
var assembleAndroidTestTask : String? = "assembleDebugAndroidTest"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opening these up to be nullable. If they are set to null, we'll ignore that module when creating the main task.

var runAndroidTestTask : String? = "connectedDebugAndroidTest"
var jvmTestTask : String? = "testDebugUnitTest"

companion object {
const val name = "affectedTestConfiguration"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,27 +81,27 @@ class AffectedModuleDetectorPluginTest {
tasks.forEach { taskName ->
val task = rootProject.tasks.findByName(taskName)
assertThat(task).isNotNull()
assertThat(task?.enabled).isFalse()
}
}

@Test
fun `GIVEN root project WHEN plugin is applied and child has configuration THEN tasks are added`() {
// GIVEN
val taskName = "assembleAffectedAndroidTests"
childProject.tasks.register("assembleDebugAndroidTest")
childProject.tasks.register("myfaketask")

// WHEN
rootProject.pluginManager.apply(AffectedModuleDetectorPlugin::class.java)
val default = rootProject as DefaultProject
default.evaluate()
val ext = requireNotNull(childProject.extensions.findByType(AffectedTestConfiguration::class.java))
ext.jvmTestTask = "myfaketest"

(rootProject as DefaultProject).evaluate()

// THEN
val task = rootProject.tasks.findByName(taskName)
assertThat(task).isNotNull()
assertThat(task?.enabled).isTrue()
task?.dependsOn?.forEach { dependency ->
assertThat(dependency).isEqualTo(":child:assembleDebugAndroidTest")
val task = requireNotNull(rootProject.tasks.findByName(taskName))
assertThat(task.enabled).isTrue()
task.dependsOn.forEach { dependency ->
assertThat(dependency).isEqualTo(":child:myfaketask")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,71 +14,28 @@ class AffectedTestConfigurationTest {
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN default value of variant to test THEN debug is returned`() {
assertThat(config.variantToTest).isEqualTo("debug")
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN variant to test is set THEN value is returned`() {
// GIVEN
val sample = "sample"

// WHEN
config.variantToTest = sample

// THEN
assertThat(config.variantToTest).isEqualTo(sample)
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN assemble android test task is called THEN default is returned`() {
fun `GIVEN AffectedTestConfiguration WHEN default values THEN default values returned`() {
assertThat(config.assembleAndroidTestTask).isEqualTo("assembleDebugAndroidTest")
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN variant is setup and assemble android test task is called THEN variant task is returned`() {
// GIVEN
config.variantToTest = "sample"

// WHEN
val task = config.assembleAndroidTestTask

// THEN
assertThat(task).isEqualTo("assembleSampleAndroidTest")
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN run android test task is called THEN default is returned`() {
assertThat(config.runAndroidTestTask).isEqualTo("connectedDebugAndroidTest")
assertThat(config.jvmTestTask).isEqualTo("testDebugUnitTest")
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN variant is setup and run android test task is called THEN variant task is returned`() {
// GIVEN
config.variantToTest = "sample"

// WHEN
val task = config.runAndroidTestTask

// THEN
assertThat(task).isEqualTo("connectedSampleAndroidTest")
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN jvm test is called THEN default is returned`() {
assertThat(config.jvmTest).isEqualTo("testDebugUnitTest")
}

@Test
fun `GIVEN AffectedTestConfiguration WHEN variant is setup and jvm test is called THEN variant task is returned`() {
fun `GIVEN AffectedTestConfiguration WHEN values are updated THEN new values are returned`() {
// GIVEN
config.variantToTest = "sample"
val assembleAndroidTestTask = "assembleAndroidTestTask"
val runAndroidTestTask = "runAndroidTestTask"
val jvmTest = "jvmTest"

// WHEN
val task = config.jvmTest
config.assembleAndroidTestTask = assembleAndroidTestTask
config.runAndroidTestTask = runAndroidTestTask
config.jvmTestTask = jvmTest

// THEN
assertThat(task).isEqualTo("testSampleUnitTest")
assertThat(config.assembleAndroidTestTask).isEqualTo(assembleAndroidTestTask)
assertThat(config.runAndroidTestTask).isEqualTo(runAndroidTestTask)
assertThat(config.jvmTestTask).isEqualTo(jvmTest)
}

@Test
Expand Down
2 changes: 1 addition & 1 deletion gradle/releasing.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ext {
DESCRIPTION = "A Gradle Plugin and library to determine which modules were affected in a commit."
VERSION = "0.1.1-SNAPSHOT"
VERSION = "0.1.2-SNAPSHOT"
GIT_URL = 'https://github.com/Dropbox/AffectedModuleDetector'
GROUP_ID = "com.dropbox.affectedmoduledetector"
SONATYPE_SNAPSHOT_URL = "https://oss.sonatype.org/content/repositories/snapshots/"
Expand Down
4 changes: 2 additions & 2 deletions sample/sample-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ plugins {
id 'kotlin-android'
}

affectedTestConfiguration{
variantToTest = "debug"
affectedTestConfiguration {
assembleAndroidTestTask = "assembleAndroidTest"
}


Expand Down
5 changes: 3 additions & 2 deletions sample/sample-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ plugins {
id 'com.android.library'
id 'kotlin-android'
}
affectedTestConfiguration{
variantToTest = "debug"

affectedTestConfiguration {
jvmTest = "test"
}

android {
Expand Down
4 changes: 4 additions & 0 deletions sample/sample-util/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ plugins {
id 'kotlin-android'
}

affectedTestConfiguration {
assembleAndroidTestTask = "assembleDebugAndroidTest"
}

android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
Expand Down