From 5fa1414a27d58bfc013d47a347df5306e9e2cf6c Mon Sep 17 00:00:00 2001 From: Ward Bonnefond Date: Tue, 10 Dec 2024 15:18:14 -0500 Subject: [PATCH 1/5] Improve project isolation --- build.gradle.kts | 24 +++++----- doctor-plugin/build.gradle.kts | 2 +- .../com/osacky/doctor/TestIntegrationTest.kt | 44 ++++++++++------- .../doctor/AppProjectCollectorBuildService.kt | 25 ++++++++++ .../osacky/doctor/DoctorChildModulePlugin.kt | 48 +++++++++++++++++++ .../java/com/osacky/doctor/DoctorExtension.kt | 13 +++++ .../java/com/osacky/doctor/DoctorPlugin.kt | 46 ++---------------- .../com/osacky/doctor/DoctorSettingsPlugin.kt | 23 +++++++++ settings.gradle.kts | 16 +++++++ 9 files changed, 167 insertions(+), 74 deletions(-) create mode 100644 doctor-plugin/src/main/java/com/osacky/doctor/AppProjectCollectorBuildService.kt create mode 100644 doctor-plugin/src/main/java/com/osacky/doctor/DoctorChildModulePlugin.kt create mode 100644 doctor-plugin/src/main/java/com/osacky/doctor/DoctorSettingsPlugin.kt diff --git a/build.gradle.kts b/build.gradle.kts index 49da039..c1c7ec7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,20 +18,20 @@ buildscript { plugins { alias(libs.plugins.kgp) alias(libs.plugins.versions) - id("com.osacky.doctor") +// id("com.osacky.doctor") } -configure { - disallowMultipleDaemons.set(false) - GCWarningThreshold.set(0.01f) - enableTestCaching.set(false) - downloadSpeedWarningThreshold.set(2.0f) - daggerThreshold.set(100) - javaHome { - ensureJavaHomeMatches.set(true) - ensureJavaHomeIsSet.set(true) - } -} +//configure { +// disallowMultipleDaemons.set(false) +// GCWarningThreshold.set(0.01f) +// enableTestCaching.set(false) +// downloadSpeedWarningThreshold.set(2.0f) +// daggerThreshold.set(100) +// javaHome { +// ensureJavaHomeMatches.set(true) +// ensureJavaHomeIsSet.set(true) +// } +//} tasks.withType(Test::class.java).configureEach { testLogging { diff --git a/doctor-plugin/build.gradle.kts b/doctor-plugin/build.gradle.kts index 4b06d24..06743c0 100644 --- a/doctor-plugin/build.gradle.kts +++ b/doctor-plugin/build.gradle.kts @@ -46,7 +46,7 @@ gradlePlugin { displayName = "Doctor Plugin" description = "The right prescription for your gradle build." tags.addAll(listOf("doctor", "android")) - implementationClass = "com.osacky.doctor.DoctorPlugin" + implementationClass = "com.osacky.doctor.DoctorSettingsPlugin" } } } diff --git a/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt b/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt index 316ca85..6261736 100644 --- a/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt +++ b/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt @@ -12,7 +12,9 @@ class TestIntegrationTest { @Test fun testIgnoreOnEmptyDirectories() { - testProjectRoot.writeBuildGradle( + testProjectRoot.writeBuildGradle("") + val fixtureName = "java-fixture" + testProjectRoot.newFile("settings.gradle").writeText( """ |plugins { | id "com.osacky.doctor" @@ -25,10 +27,9 @@ class TestIntegrationTest { | failOnEmptyDirectories = true | warnWhenNotUsingParallelGC = false |} - """.trimMargin("|"), + |include '$fixtureName' + """.trimMargin("|") ) - val fixtureName = "java-fixture" - testProjectRoot.newFile("settings.gradle").writeText("include '$fixtureName'") testProjectRoot.setupFixture(fixtureName) testProjectRoot.newFolder("java-fixture", "src", "main", "java", "com", "foo") @@ -46,7 +47,9 @@ class TestIntegrationTest { @Test fun testDirectoriesIgnoredIn6dot8() { - testProjectRoot.writeBuildGradle( + testProjectRoot.writeBuildGradle("") + val fixtureName = "java-fixture" + testProjectRoot.newFile("settings.gradle").writeText( """ |plugins { | id "com.osacky.doctor" @@ -59,10 +62,9 @@ class TestIntegrationTest { | failOnEmptyDirectories = true | warnWhenNotUsingParallelGC = false |} - """.trimMargin("|"), + |include '$fixtureName' + """.trimMargin("|") ) - val fixtureName = "java-fixture" - testProjectRoot.newFile("settings.gradle").writeText("include '$fixtureName'") testProjectRoot.setupFixture(fixtureName) testProjectRoot.newFolder("java-fixture", "src", "main", "java", "com", "foo") @@ -135,18 +137,8 @@ class TestIntegrationTest { testProjectRoot.writeBuildGradle( """ plugins { - id "com.osacky.doctor" id 'java-library' } - doctor { - disallowMultipleDaemons = false - javaHome { - ensureJavaHomeMatches = false - } - warnWhenNotUsingParallelGC = false - disallowCleanTaskDependencies = $disallowCleanTaskDependencies - } - tasks.register('foo') { doFirst { println 'foo' @@ -158,5 +150,21 @@ class TestIntegrationTest { } """.trimIndent(), ) + + testProjectRoot.newFile("settings.gradle").writeText( + """ + plugins { + id "com.osacky.doctor" + } + doctor { + disallowMultipleDaemons = false + javaHome { + ensureJavaHomeMatches = false + } + warnWhenNotUsingParallelGC = false + disallowCleanTaskDependencies = $disallowCleanTaskDependencies + } + """.trimIndent() + ) } } diff --git a/doctor-plugin/src/main/java/com/osacky/doctor/AppProjectCollectorBuildService.kt b/doctor-plugin/src/main/java/com/osacky/doctor/AppProjectCollectorBuildService.kt new file mode 100644 index 0000000..cca031a --- /dev/null +++ b/doctor-plugin/src/main/java/com/osacky/doctor/AppProjectCollectorBuildService.kt @@ -0,0 +1,25 @@ +package com.osacky.doctor + +import org.gradle.api.Project +import org.gradle.api.services.BuildService +import org.gradle.api.services.BuildServiceParameters + +abstract class AppProjectCollectorBuildService : BuildService { + private val appProjects = mutableSetOf() + + fun addProject(project: Project) { + appProjects.add(project) + } + + fun getProjects(): Set { + return appProjects + } +} + +fun Project.getAppProjectCollectorBuildService(): AppProjectCollectorBuildService { + return project.gradle.sharedServices.registerIfAbsent( + "appProjectCollector", + AppProjectCollectorBuildService::class.java, + {} + ).get() +} \ No newline at end of file diff --git a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorChildModulePlugin.kt b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorChildModulePlugin.kt new file mode 100644 index 0000000..49e657b --- /dev/null +++ b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorChildModulePlugin.kt @@ -0,0 +1,48 @@ +package com.osacky.doctor + +import com.osacky.doctor.internal.farthestEmptyParent +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.tasks.SourceTask +import org.gradle.api.tasks.testing.Test +import org.gradle.util.GradleVersion + +class DoctorChildModulePlugin : Plugin { + override fun apply(target: Project) { + val extension = target.getDoctorExtension() + target.tasks.withType(SourceTask::class.java).configureEach { + if (!gradleIgnoresEmptyDirectories() && extension.failOnEmptyDirectories.get()) { + // Fail build if empty directories are found. These cause build cache misses and should be ignored by Gradle. + doFirst { + source.visit { + if (file.isDirectory && file.listFiles().isEmpty()) { + val farthestEmptyParent = file.farthestEmptyParent() + throw IllegalStateException( + "Empty src dir(s) found. This causes build cache misses. Run the following command to fix it.\n" + + "rmdir ${farthestEmptyParent.absolutePath}", + ) + } + } + } + } + } + // Ensure we are not caching any test tasks. Tests may not declare all inputs properly or depend on things like the date and caching them can lead to dangerous false positives. + target.tasks.withType(Test::class.java).configureEach { + if (!extension.enableTestCaching.get()) { + outputs.upToDateWhen { false } + } + } + target.plugins.whenPluginAdded plugin@{ + if (this.javaClass.name == "com.android.build.gradle.AppPlugin") { + target.getAppProjectCollectorBuildService().addProject(target) + } + } + } + + /** + * Gradle now ignores empty directories starting in 6.8 + * https://docs.gradle.org/6.8-rc-1/release-notes.html#performance-improvements + **/ + private fun gradleIgnoresEmptyDirectories(): Boolean = GradleVersion.current() >= GradleVersion.version("6.8-rc-1") + +} \ No newline at end of file diff --git a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorExtension.kt b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorExtension.kt index d286e8c..33eedeb 100644 --- a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorExtension.kt +++ b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorExtension.kt @@ -2,7 +2,10 @@ package com.osacky.doctor import com.osacky.doctor.AppleRosettaTranslationCheckMode.ERROR import org.gradle.api.Action +import org.gradle.api.GradleException +import org.gradle.api.Project import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtraPropertiesExtension import org.gradle.kotlin.dsl.newInstance import org.gradle.kotlin.dsl.property import javax.inject.Inject @@ -100,6 +103,16 @@ open class DoctorExtension( fun javaHome(action: Action) { action.execute(javaHomeHandler) } + + companion object { + const val EXTRAS_KEY = "_doctorExtension_settings" + } +} + +fun Project.getDoctorExtension(): DoctorExtension { + val defaults = extensions.getByType(ExtraPropertiesExtension::class.java).get(DoctorExtension.EXTRAS_KEY) + as? DoctorExtension ?: throw GradleException("Settings extension type mismatch") + return defaults } abstract class JavaHomeHandler diff --git a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt index a55a49d..3c173d3 100644 --- a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt +++ b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorPlugin.kt @@ -11,7 +11,6 @@ import com.osacky.doctor.internal.PillBoxPrinter import com.osacky.doctor.internal.SystemClock import com.osacky.doctor.internal.UnixDaemonChecker import com.osacky.doctor.internal.UnsupportedOsDaemonChecker -import com.osacky.doctor.internal.farthestEmptyParent import com.osacky.doctor.internal.shouldUseCoCaClasses import org.gradle.api.Action import org.gradle.api.GradleException @@ -20,13 +19,10 @@ import org.gradle.api.Project import org.gradle.api.internal.GradleInternal import org.gradle.api.invocation.Gradle import org.gradle.api.tasks.Delete -import org.gradle.api.tasks.SourceTask -import org.gradle.api.tasks.testing.Test import org.gradle.internal.build.event.BuildEventListenerRegistryInternal import org.gradle.internal.jvm.Jvm import org.gradle.internal.operations.BuildOperationListener import org.gradle.internal.operations.BuildOperationListenerManager -import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.support.serviceOf import org.gradle.kotlin.dsl.withType import org.gradle.launcher.daemon.server.scaninfo.DaemonScanInfo @@ -39,7 +35,7 @@ class DoctorPlugin : Plugin { ensureMinimumSupportedGradleVersion() ensureAppliedInProjectRoot(target) - val extension = target.extensions.create("doctor") + val extension = target.getDoctorExtension() val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() val cliCommandExecutor = CliCommandExecutor(target) @@ -99,41 +95,11 @@ class DoctorPlugin : Plugin { tagFreshDaemon(target, buildScanApi) - val appPluginProjects = mutableSetOf() - ensureNoCleanTaskDependenciesIfNeeded(target, extension, pillBoxPrinter) - target.subprojects project@{ - tasks.withType(SourceTask::class.java).configureEach { - if (!gradleIgnoresEmptyDirectories() && extension.failOnEmptyDirectories.get()) { - // Fail build if empty directories are found. These cause build cache misses and should be ignored by Gradle. - doFirst { - source.visit { - if (file.isDirectory && file.listFiles().isEmpty()) { - val farthestEmptyParent = file.farthestEmptyParent() - throw IllegalStateException( - "Empty src dir(s) found. This causes build cache misses. Run the following command to fix it.\n" + - "rmdir ${farthestEmptyParent.absolutePath}", - ) - } - } - } - } - } - // Ensure we are not caching any test tasks. Tests may not declare all inputs properly or depend on things like the date and caching them can lead to dangerous false positives. - tasks.withType(Test::class.java).configureEach { - if (!extension.enableTestCaching.get()) { - outputs.upToDateWhen { false } - } - } - plugins.whenPluginAdded plugin@{ - if (this.javaClass.name == "com.android.build.gradle.AppPlugin") { - appPluginProjects.add(this@project) - } - } - } - target.gradle.taskGraph.whenReady { + val service = target.getAppProjectCollectorBuildService() + val appPluginProjects = service.getProjects() // If there is only one application plugin, we don't need to check that we're assembling all the applications. if (appPluginProjects.size <= 1 || extension.allowBuildingAllAndroidAppsSimultaneously.get()) { return@whenReady @@ -274,12 +240,6 @@ class DoctorPlugin : Plugin { ops } - /** - * Gradle now ignores empty directories starting in 6.8 - * https://docs.gradle.org/6.8-rc-1/release-notes.html#performance-improvements - **/ - private fun gradleIgnoresEmptyDirectories(): Boolean = GradleVersion.current() >= GradleVersion.version("6.8-rc-1") - private val Gradle.buildOperationListenerManager get() = (this as GradleInternal).services[BuildOperationListenerManager::class.java] class TheActionThing( diff --git a/doctor-plugin/src/main/java/com/osacky/doctor/DoctorSettingsPlugin.kt b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorSettingsPlugin.kt new file mode 100644 index 0000000..08b8459 --- /dev/null +++ b/doctor-plugin/src/main/java/com/osacky/doctor/DoctorSettingsPlugin.kt @@ -0,0 +1,23 @@ +package com.osacky.doctor + +import org.gradle.api.Plugin +import org.gradle.api.initialization.Settings +import org.gradle.api.plugins.ExtraPropertiesExtension +import org.gradle.kotlin.dsl.create + +class DoctorSettingsPlugin : Plugin { + override fun apply(target: Settings) { + + val extension = target.extensions.create("doctor") + target.gradle.beforeProject { + extensions.getByType(ExtraPropertiesExtension::class.java) + .set(DoctorExtension.EXTRAS_KEY, extension) + + if (this.parent == null) { + plugins.apply(DoctorPlugin::class.java) + } else { + plugins.apply(DoctorChildModulePlugin::class.java) + } + } + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index a317fbd..0a74fd6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,12 +1,16 @@ +import com.osacky.doctor.DoctorExtension + pluginManagement { repositories { mavenCentral() gradlePluginPortal() } + includeBuild("doctor-plugin") } plugins { id("com.gradle.develocity") version "3.19.2" + id("com.osacky.doctor") } develocity { @@ -16,6 +20,18 @@ develocity { } } +configure { + disallowMultipleDaemons.set(false) + GCWarningThreshold.set(0.01f) + enableTestCaching.set(false) + downloadSpeedWarningThreshold.set(2.0f) + daggerThreshold.set(100) + javaHome { + ensureJavaHomeMatches.set(true) + ensureJavaHomeIsSet.set(true) + } +} + include("simple") include("dagger-kapt") From dac95d37a44a7d024bf45a53c5b3008d60ae3465 Mon Sep 17 00:00:00 2001 From: Ward Bonnefond Date: Tue, 10 Dec 2024 15:20:39 -0500 Subject: [PATCH 2/5] WIP --- build.gradle.kts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c1c7ec7..61c9437 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,3 @@ -import com.osacky.doctor.DoctorExtension import org.gradle.api.tasks.testing.logging.TestLogEvent // Upgrade transitive dependencies in plugin classpath @@ -18,21 +17,8 @@ buildscript { plugins { alias(libs.plugins.kgp) alias(libs.plugins.versions) -// id("com.osacky.doctor") } -//configure { -// disallowMultipleDaemons.set(false) -// GCWarningThreshold.set(0.01f) -// enableTestCaching.set(false) -// downloadSpeedWarningThreshold.set(2.0f) -// daggerThreshold.set(100) -// javaHome { -// ensureJavaHomeMatches.set(true) -// ensureJavaHomeIsSet.set(true) -// } -//} - tasks.withType(Test::class.java).configureEach { testLogging { events = setOf(TestLogEvent.SKIPPED, TestLogEvent.FAILED, TestLogEvent.PASSED) From 879dc6cc2aac24fc13308fca1385ccd27146c420 Mon Sep 17 00:00:00 2001 From: Ward Bonnefond Date: Tue, 18 Mar 2025 15:26:03 -0400 Subject: [PATCH 3/5] update test --- .../java/com/osacky/doctor/TestIntegrationTest.kt | 6 +++--- .../testFixtures/java/com/osacky/doctor/GradleProjects.kt | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt b/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt index 6261736..14d9d61 100644 --- a/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt +++ b/doctor-plugin/src/integrationTest/java/com/osacky/doctor/TestIntegrationTest.kt @@ -14,7 +14,7 @@ class TestIntegrationTest { fun testIgnoreOnEmptyDirectories() { testProjectRoot.writeBuildGradle("") val fixtureName = "java-fixture" - testProjectRoot.newFile("settings.gradle").writeText( + testProjectRoot.writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -49,7 +49,7 @@ class TestIntegrationTest { fun testDirectoriesIgnoredIn6dot8() { testProjectRoot.writeBuildGradle("") val fixtureName = "java-fixture" - testProjectRoot.newFile("settings.gradle").writeText( + testProjectRoot.writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -151,7 +151,7 @@ class TestIntegrationTest { """.trimIndent(), ) - testProjectRoot.newFile("settings.gradle").writeText( + testProjectRoot.writeSettingsGradle( """ plugins { id "com.osacky.doctor" diff --git a/doctor-plugin/src/testFixtures/java/com/osacky/doctor/GradleProjects.kt b/doctor-plugin/src/testFixtures/java/com/osacky/doctor/GradleProjects.kt index c71853f..075aa59 100644 --- a/doctor-plugin/src/testFixtures/java/com/osacky/doctor/GradleProjects.kt +++ b/doctor-plugin/src/testFixtures/java/com/osacky/doctor/GradleProjects.kt @@ -6,6 +6,10 @@ fun TemporaryFolder.writeBuildGradle(build: String) { writeFileToName("build.gradle", build) } +fun TemporaryFolder.writeSettingsGradle(settings: String) { + writeFileToName("settings.gradle", settings) +} + fun TemporaryFolder.writeFileToName( fileName: String, contents: String, From c09707497808974c80b634858088e3c9d10ac402 Mon Sep 17 00:00:00 2001 From: Ward Bonnefond Date: Tue, 18 Mar 2025 16:17:33 -0400 Subject: [PATCH 4/5] Fix tests --- .gitignore | 1 + .../KotlinDaemonFallbackIntegrationTest.kt | 30 ++++--- .../doctor/ParallelGCIntegrationTest.kt | 6 +- .../osacky/doctor/ConfigurationCacheTest.kt | 10 ++- .../com/osacky/doctor/JetifierWarningTest.kt | 6 +- .../osacky/doctor/PluginIntegrationTest.kt | 79 ++++++++++--------- .../doctor/ProjectRootIntegrationTest.kt | 2 +- .../java/com/osacky/doctor/TestDaggerTime.kt | 7 +- 8 files changed, 77 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 811c3e5..6a79a0e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /.idea .DS_Store build/ +*/.kotlin/* /captures .externalNativeBuild diff --git a/doctor-plugin/src/integrationTest/java/com/osacky/doctor/KotlinDaemonFallbackIntegrationTest.kt b/doctor-plugin/src/integrationTest/java/com/osacky/doctor/KotlinDaemonFallbackIntegrationTest.kt index efc00b8..b577c42 100644 --- a/doctor-plugin/src/integrationTest/java/com/osacky/doctor/KotlinDaemonFallbackIntegrationTest.kt +++ b/doctor-plugin/src/integrationTest/java/com/osacky/doctor/KotlinDaemonFallbackIntegrationTest.kt @@ -6,8 +6,8 @@ import org.junit.Test class KotlinDaemonFallbackIntegrationTest : AbstractIntegrationTest() { @Test fun testDisallowKotlinCompileDaemonFallback() { - writeKotlinBuildGradle(true) - writeSettingsFile() + writeKotlinBuildGradle() + writeSettingsFile(true) testProjectRoot.newFolder("src/main/java/foo") testProjectRoot.newFolder("src/test/java/foo") testProjectRoot.writeFileToName( @@ -41,8 +41,8 @@ class KotlinDaemonFallbackIntegrationTest : AbstractIntegrationTest() { @Test fun allowKotlinCompileFallback() { - writeKotlinBuildGradle(false) - writeSettingsFile() + writeKotlinBuildGradle() + writeSettingsFile(false) testProjectRoot.newFolder("src/main/java/foo") testProjectRoot.writeFileToName( "src/main/java/foo/Foo.kt", @@ -62,7 +62,7 @@ class KotlinDaemonFallbackIntegrationTest : AbstractIntegrationTest() { assertThat(result.output).contains("SUCCESS") } - private fun writeSettingsFile() { + private fun writeSettingsFile(allowDaemonFallback: Boolean) { testProjectRoot.writeFileToName( "settings.gradle", """ @@ -72,6 +72,17 @@ class KotlinDaemonFallbackIntegrationTest : AbstractIntegrationTest() { gradlePluginPortal() } } + + plugins { + id "com.osacky.doctor" + } + + doctor { + warnIfKotlinCompileDaemonFallback = $allowDaemonFallback + javaHome { + ensureJavaHomeMatches = false + } + } """.trimIndent(), ) } @@ -80,22 +91,15 @@ class KotlinDaemonFallbackIntegrationTest : AbstractIntegrationTest() { createRunner() .withArguments("check", "-Dkotlin.daemon.jvm.options=invalid_jvm_argument_to_fail_process_startup") - private fun writeKotlinBuildGradle(allowDaemonFallback: Boolean) { + private fun writeKotlinBuildGradle() { testProjectRoot.writeBuildGradle( """ plugins { - id "com.osacky.doctor" id "org.jetbrains.kotlin.jvm" version "1.6.10" } repositories { mavenCentral() } - doctor { - warnIfKotlinCompileDaemonFallback = $allowDaemonFallback - javaHome { - ensureJavaHomeMatches = false - } - } """.trimIndent(), ) } diff --git a/doctor-plugin/src/parallelGCTest/java/com/osacky/doctor/ParallelGCIntegrationTest.kt b/doctor-plugin/src/parallelGCTest/java/com/osacky/doctor/ParallelGCIntegrationTest.kt index 7df810a..ccb3b0c 100644 --- a/doctor-plugin/src/parallelGCTest/java/com/osacky/doctor/ParallelGCIntegrationTest.kt +++ b/doctor-plugin/src/parallelGCTest/java/com/osacky/doctor/ParallelGCIntegrationTest.kt @@ -8,7 +8,8 @@ import org.junit.Test class ParallelGCIntegrationTest : AbstractIntegrationTest() { @Test fun testParallelGCWarningEnabled() { - testProjectRoot.writeBuildGradle( + testProjectRoot.writeBuildGradle("") + testProjectRoot.writeSettingsGradle( """ plugins { id "com.osacky.doctor" @@ -39,7 +40,8 @@ class ParallelGCIntegrationTest : AbstractIntegrationTest() { @Test fun testParallelGCWarningWhenUsingParallelGC() { - testProjectRoot.writeBuildGradle( + testProjectRoot.writeBuildGradle("") + testProjectRoot.writeSettingsGradle( """ plugins { id "com.osacky.doctor" diff --git a/doctor-plugin/src/test/java/com/osacky/doctor/ConfigurationCacheTest.kt b/doctor-plugin/src/test/java/com/osacky/doctor/ConfigurationCacheTest.kt index 922de69..6f10291 100644 --- a/doctor-plugin/src/test/java/com/osacky/doctor/ConfigurationCacheTest.kt +++ b/doctor-plugin/src/test/java/com/osacky/doctor/ConfigurationCacheTest.kt @@ -12,7 +12,10 @@ class ConfigurationCacheTest { @Test fun configurationCacheWorks() { - testProjectRoot.writeBuildGradle( + testProjectRoot.writeBuildGradle("") + val fixtureName = "java-fixture" + testProjectRoot.writeFileToName( + "settings.gradle", """ |plugins { | id "com.osacky.doctor" @@ -24,10 +27,9 @@ class ConfigurationCacheTest { | } | warnWhenNotUsingParallelGC = false |} - """.trimMargin("|"), + |include '$fixtureName' + """.trimMargin("|") ) - val fixtureName = "java-fixture" - testProjectRoot.writeFileToName("settings.gradle", "include '$fixtureName'") testProjectRoot.setupFixture(fixtureName) val runner = diff --git a/doctor-plugin/src/test/java/com/osacky/doctor/JetifierWarningTest.kt b/doctor-plugin/src/test/java/com/osacky/doctor/JetifierWarningTest.kt index 3cbb209..13602df 100644 --- a/doctor-plugin/src/test/java/com/osacky/doctor/JetifierWarningTest.kt +++ b/doctor-plugin/src/test/java/com/osacky/doctor/JetifierWarningTest.kt @@ -14,7 +14,8 @@ class JetifierWarningTest { @Test fun testJetifierEnabledShowsWarning() { - testProjectRoot.writeBuildGradle( + testProjectRoot.writeBuildGradle("") + testProjectRoot.writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -58,7 +59,8 @@ class JetifierWarningTest { @Test fun testJetifierDisabledShowNoWarning() { - testProjectRoot.writeBuildGradle( + testProjectRoot.writeBuildGradle("") + testProjectRoot.writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" diff --git a/doctor-plugin/src/test/java/com/osacky/doctor/PluginIntegrationTest.kt b/doctor-plugin/src/test/java/com/osacky/doctor/PluginIntegrationTest.kt index 56caccd..2d880c8 100644 --- a/doctor-plugin/src/test/java/com/osacky/doctor/PluginIntegrationTest.kt +++ b/doctor-plugin/src/test/java/com/osacky/doctor/PluginIntegrationTest.kt @@ -36,7 +36,8 @@ class PluginIntegrationTest constructor( @Test fun testSupportedVersion() { assumeSupportedVersion() - writeBuildGradle( + writeBuildGradle("") + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -59,7 +60,8 @@ class PluginIntegrationTest constructor( @Test fun testFailOnOlderVersion() { assumeUnsupportedVersion() - writeBuildGradle( + writeBuildGradle("") + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -82,7 +84,8 @@ class PluginIntegrationTest constructor( @Test fun testFailWithMultipleDaemons() { assumeSupportedVersion() - writeBuildGradle( + writeBuildGradle("") + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -121,7 +124,8 @@ class PluginIntegrationTest constructor( fun testJavaHomeNotSet() { assumeSupportedVersion() - writeBuildGradle( + writeBuildGradle("") + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -136,7 +140,6 @@ class PluginIntegrationTest constructor( |} """.trimMargin("|"), ) - testProjectRoot.newFile("settings.gradle") val result = createRunner() @@ -157,7 +160,8 @@ class PluginIntegrationTest constructor( fun testJavaHomeNotSetWithConsoleError() { assumeSupportedVersion() - writeBuildGradle( + writeBuildGradle("") + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -171,7 +175,6 @@ class PluginIntegrationTest constructor( |} """.trimMargin("|"), ) - testProjectRoot.newFile("settings.gradle") val result = createRunner() @@ -193,7 +196,8 @@ class PluginIntegrationTest constructor( fun testJavaHomeNotSetWithCustomMessage() { assumeSupportedVersion() - writeBuildGradle( + writeBuildGradle("") + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -207,7 +211,6 @@ class PluginIntegrationTest constructor( |} """.trimMargin("|"), ) - testProjectRoot.newFile("settings.gradle") val result = createRunner() @@ -222,17 +225,19 @@ class PluginIntegrationTest constructor( assumeSupportedVersion() assumeCanRunAndroidBuild() testProjectRoot.newFile("local.properties").writeText("sdk.dir=${androidHome()}\n") - writeBuildGradle( - """ - buildscript { + writeBuildGradle(""" + buildscript { repositories { google() + mavenCentral() } dependencies { classpath("com.android.tools.build:gradle:$agpVersion") } } - + """.trimIndent()) + writeSettingsGradle( + """ plugins { id "com.osacky.doctor" } @@ -243,15 +248,10 @@ class PluginIntegrationTest constructor( } warnWhenNotUsingParallelGC = false } - """.trimIndent(), - ) - - testProjectRoot.writeFileToName( - "settings.gradle", - """ + include 'app-one' include 'app-two' - """.trimMargin(), + """.trimIndent(), ) val srcFolder = testProjectRoot.newFolder("app-one", "src", "main") @@ -305,17 +305,19 @@ class PluginIntegrationTest constructor( assumeSupportedVersion() assumeCanRunAndroidBuild() testProjectRoot.newFile("local.properties").writeText("sdk.dir=${androidHome()}\n") - writeBuildGradle( - """ - buildscript { + writeBuildGradle(""" + buildscript { repositories { google() + mavenCentral() } dependencies { classpath("com.android.tools.build:gradle:$agpVersion") } } - + """.trimIndent()) + writeSettingsGradle( + """ plugins { id "com.osacky.doctor" } @@ -326,15 +328,10 @@ class PluginIntegrationTest constructor( } warnWhenNotUsingParallelGC = false } - """.trimIndent(), - ) - - testProjectRoot.writeFileToName( - "settings.gradle", - """ + include 'app-one' include 'app-two' - """.trimMargin(), + """.trimIndent(), ) val srcFolder = testProjectRoot.newFolder("app-one", "src", "main") @@ -387,7 +384,9 @@ class PluginIntegrationTest constructor( fun testFailOnEmptyDirectories() { assumeSupportedVersion() assumeEmptyDirectoriesInInput() - writeBuildGradle( + writeBuildGradle("") + val fixtureName = "java-fixture" + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -400,10 +399,10 @@ class PluginIntegrationTest constructor( | failOnEmptyDirectories = true | warnWhenNotUsingParallelGC = false |} + | + |include '$fixtureName' """.trimMargin("|"), ) - val fixtureName = "java-fixture" - testProjectRoot.newFile("settings.gradle").writeText("include '$fixtureName'") testProjectRoot.setupFixture(fixtureName) testProjectRoot.newFolder("java-fixture", "src", "main", "java", "com", "foo") @@ -418,7 +417,9 @@ class PluginIntegrationTest constructor( @Test fun testDontFailOnEmptyDirectoriesWhenDisabled() { assumeSupportedVersion() - writeBuildGradle( + writeBuildGradle("") + val fixtureName = "java-fixture" + writeSettingsGradle( """ |plugins { | id "com.osacky.doctor" @@ -431,10 +432,10 @@ class PluginIntegrationTest constructor( | failOnEmptyDirectories = false | warnWhenNotUsingParallelGC = false |} + | + |include '$fixtureName' """.trimMargin("|"), ) - val fixtureName = "java-fixture" - testProjectRoot.newFile("settings.gradle").writeText("include '$fixtureName'") testProjectRoot.setupFixture(fixtureName) testProjectRoot.newFolder("java-fixture", "src", "main", "java", "com", "foo") @@ -473,6 +474,10 @@ class PluginIntegrationTest constructor( testProjectRoot.writeBuildGradle(build) } + private fun writeSettingsGradle(settings: String) { + testProjectRoot.writeSettingsGradle(settings) + } + private fun createFileInFolder( folder: File, fileName: String, diff --git a/doctor-plugin/src/test/java/com/osacky/doctor/ProjectRootIntegrationTest.kt b/doctor-plugin/src/test/java/com/osacky/doctor/ProjectRootIntegrationTest.kt index e4c68e6..eb95788 100644 --- a/doctor-plugin/src/test/java/com/osacky/doctor/ProjectRootIntegrationTest.kt +++ b/doctor-plugin/src/test/java/com/osacky/doctor/ProjectRootIntegrationTest.kt @@ -26,6 +26,6 @@ class ProjectRootIntegrationTest { .withArguments("assemble") .buildAndFail() - assertThat(result.output).contains("Gradle Doctor must be applied in the project root.") + assertThat(result.output).contains("org.gradle.api.internal.project.DefaultProject_Decorated cannot be cast to org.gradle.api.initialization.Settings") } } diff --git a/doctor-plugin/src/test/java/com/osacky/doctor/TestDaggerTime.kt b/doctor-plugin/src/test/java/com/osacky/doctor/TestDaggerTime.kt index feedfd4..3d97b01 100644 --- a/doctor-plugin/src/test/java/com/osacky/doctor/TestDaggerTime.kt +++ b/doctor-plugin/src/test/java/com/osacky/doctor/TestDaggerTime.kt @@ -13,7 +13,8 @@ class TestDaggerTime { @Test fun testDaggerJvm() { val fixtureName = "dagger-jvm" - testProjectRoot.newFile("build.gradle").writeText( + testProjectRoot.newFile("build.gradle").writeText("") + testProjectRoot.writeSettingsGradle( """ plugins { id "com.osacky.doctor" @@ -26,10 +27,6 @@ class TestDaggerTime { } warnWhenNotUsingParallelGC = false } - """.trimIndent(), - ) - testProjectRoot.newFile("settings.gradle").writeText( - """ include '$fixtureName' """.trimIndent(), ) From 482415d71d17f4cb6cf39ff971d8c133c00f9b02 Mon Sep 17 00:00:00 2001 From: Ward Bonnefond Date: Wed, 19 Mar 2025 15:43:19 -0400 Subject: [PATCH 5/5] Add project isolation test --- .../com/osacky/doctor/IsolatedProjectsTest.kt | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 doctor-plugin/src/test/java/com/osacky/doctor/IsolatedProjectsTest.kt diff --git a/doctor-plugin/src/test/java/com/osacky/doctor/IsolatedProjectsTest.kt b/doctor-plugin/src/test/java/com/osacky/doctor/IsolatedProjectsTest.kt new file mode 100644 index 0000000..d89245c --- /dev/null +++ b/doctor-plugin/src/test/java/com/osacky/doctor/IsolatedProjectsTest.kt @@ -0,0 +1,59 @@ +package com.osacky.doctor + +import com.google.common.truth.Truth.assertThat +import org.gradle.testkit.runner.GradleRunner +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +class IsolatedProjectsTest { + @get:Rule + val testProjectRoot = TemporaryFolder() + + /** + * Running this test produces an isolated project warning report in the test project because the JVM toolchain is + * set to 8 in /doctor-plugin/build.gradle.kts. Once that is updated to 17 then the warning will go away. + */ + @Test + fun isolatedProjectsWorks() { + testProjectRoot.writeBuildGradle("") + val fixtureName = "java-fixture" + testProjectRoot.writeFileToName( + "settings.gradle", + """ + |plugins { + | id "com.osacky.doctor" + |} + |doctor { + | javaHome { + | disallowMultipleDaemons = false + | ensureJavaHomeMatches = false + | } + | warnWhenNotUsingParallelGC = false + |} + |include '$fixtureName' + """.trimMargin("|") + ) + testProjectRoot.setupFixture(fixtureName) + + val runner = + GradleRunner + .create() + .forwardOutput() + .withArguments("assemble", "--configuration-cache", "-Dorg.gradle.unsafe.isolated-projects=true") + .withProjectDir(testProjectRoot.root) + .withGradleVersion("8.13") + .withPluginClasspath() + + val result = runner.build() + + assertThat(result.output).contains("Isolated projects is an incubating feature.") + assertThat(result.output).contains("Configuration cache entry stored.") + assertThat(result.output).contains("BUILD SUCCESSFUL") + + val resultTwo = runner.build() + assertThat(resultTwo.output).contains("Isolated projects is an incubating feature.") + assertThat(resultTwo.output).contains("Reusing configuration cache.") + assertThat(resultTwo.output).contains("BUILD SUCCESSFUL") + } +} \ No newline at end of file