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

Filter by extension

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 @@ -77,10 +77,7 @@ class AffectedModuleDetectorPlugin : Plugin<Project> {
}

private fun registerCustomTasks(rootProject: Project) {
val mainConfiguration = requireNotNull(
value = rootProject.extensions.findByName(AffectedModuleConfiguration.name),
lazyMessage = { "Unable to find ${AffectedTestConfiguration.name} in $rootProject" }
) as AffectedModuleConfiguration
val mainConfiguration = requireConfiguration(rootProject)
Copy link
Contributor

Choose a reason for hiding this comment

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

this method calls quite often in big multymodule projects.

  1. too much boilerplate
  2. DRY
  3. we can do small optimization with singleton

Copy link
Member Author

Choose a reason for hiding this comment

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

After the changes to task registration this won't be called as often so that isn't a huge deal

  1. Can you explain what you mean by boilerplate? I don't see anything here out of the ordinary
  2. I understand what DRY is, this infact removes duplicate code and fixes a but committed by you (wrong configuration.name)
  3. Extensions is a map meaning looking up by name is a constant time function call, essentially the same as doing it via a singleton. Plus we don't have to worry about the lifecycle of setting the singleton

Copy link
Contributor

Choose a reason for hiding this comment

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

I recognized your comments in my previous PR. Actually, I was wrong. Thank u for the explanation.

I closed my previous PR, a bit pity my time. But it is the right way.

We can merge your version for solving the problem.


rootProject.afterEvaluate {
registerCustomTasks(rootProject, mainConfiguration.customTasks)
Expand Down Expand Up @@ -166,9 +163,15 @@ class AffectedModuleDetectorPlugin : Plugin<Project> {
testType: AffectedModuleTaskType,
project: Project
) {
val config = requireConfiguration(project)

fun isExcludedModule(configuration: AffectedModuleConfiguration, path: String): Boolean {
return configuration.excludedModules.find { path.startsWith(":$it") } != null
}

project.pluginManager.withPlugin(pluginId) {
getAffectedPath(testType, project)?.let { path ->
if (AffectedModuleDetector.isProjectProvided(project)) {
if (AffectedModuleDetector.isProjectProvided(project) && !isExcludedModule(config, path)) {
Copy link
Contributor

@Evleaps Evleaps Oct 5, 2022

Choose a reason for hiding this comment

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

we can call return before here. Calling afterEvaluate and withPlugin is not necessary in my mind.

task.dependsOn(path)
}

Expand Down Expand Up @@ -249,6 +252,13 @@ class AffectedModuleDetectorPlugin : Plugin<Project> {
}
}

private fun requireConfiguration(project: Project): AffectedModuleConfiguration {
return requireNotNull(
value = project.rootProject.extensions.findByName(AffectedModuleConfiguration.name),
lazyMessage = { "Unable to find ${AffectedModuleConfiguration.name} in ${project.rootProject}" }
) as AffectedModuleConfiguration
}

companion object {

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,82 @@ class AffectedModuleDetectorIntegrationTest {
assertThat(result.output).contains(":sample-core:assembleAndroidTest SKIPPED")
assertThat(result.output).contains(":assembleAffectedAndroidTests SKIPPED")
}

@Test
fun `GIVEN multiple project with one excluded WHEN plugin is applied THEN tasks has dependencies minus the exclusions`() {
// GIVEN
tmpFolder.newFolder("sample-app")
tmpFolder.newFolder("sample-core")
tmpFolder.newFile("settings.gradle").writeText(
"""
|include ':sample-app'
|include ':sample-core'
""".trimMargin()
)

tmpFolder.newFile("build.gradle").writeText(
"""buildscript {
| repositories {
| google()
| jcenter()
| }
| dependencies {
| classpath "com.android.tools.build:gradle:4.1.0"
| classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
| }
|}
|plugins {
| id "com.dropbox.affectedmoduledetector"
|}
|affectedModuleDetector {
| excludedModules = [ "sample-core" ]
|}
|allprojects {
| repositories {
| google()
| jcenter()
| }
|}""".trimMargin()
)

tmpFolder.newFile("sample-app/build.gradle").writeText(
"""plugins {
| id 'com.android.application'
| id 'kotlin-android'
| }
| android {
| compileSdkVersion 30
| buildToolsVersion "30.0.2"
| }
| dependencies {
| implementation project(":sample-core")
| }""".trimMargin()
)

tmpFolder.newFile("sample-core/build.gradle").writeText(
"""plugins {
| id 'com.android.library'
| id 'kotlin-android'
| }
| affectedTestConfiguration {
| assembleAndroidTestTask = "assembleAndroidTest"
| }
| android {
| compileSdkVersion 30
| buildToolsVersion "30.0.2"
| }""".trimMargin()
)

// WHEN
val result = GradleRunner.create()
.withProjectDir(tmpFolder.root)
.withPluginClasspath()
.withArguments("assembleAffectedAndroidTests", "--dry-run")
.build()

// THEN
assertThat(result.output).contains(":sample-app:assembleDebugAndroidTest SKIPPED")
assertThat(result.output).doesNotContain(":sample-core:assembleAndroidTest SKIPPED")
assertThat(result.output).contains(":assembleAffectedAndroidTests SKIPPED")
}
}