diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt index 775f9769408..260b202fab5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BaseGradleIT.kt @@ -122,7 +122,7 @@ abstract class BaseGradleIT { // gradle wrapper version to wrapper directory private val gradleWrappers = hashMapOf() - private const val MAX_DAEMON_RUNS = 30 + private const val MAX_DAEMON_RUNS = 100 private const val MAX_ACTIVE_GRADLE_PROCESSES = 1 private fun getEnvJDK_18() = System.getenv()["JDK_18"] @@ -162,6 +162,8 @@ abstract class BaseGradleIT { } if (DaemonRegistry.runCountForDaemon(version) >= MAX_DAEMON_RUNS) { + // Warning: Stopping the Gradle daemon while the test suite has not finished running can break tests in weird ways. + // TODO: Find a safe way to do this or consider deleting this code. stopDaemon(version, environmentVariables) } @@ -756,7 +758,7 @@ Finished executing task ':$taskName'| } fun CompiledProject.assertCompiledKotlinSources( - expectedSources: Iterable, + expectedSourcesRelativePaths: Iterable, weakTesting: Boolean = false, output: String = this.output, suffix: String = "" @@ -764,12 +766,15 @@ Finished executing task ':$taskName'| val messagePrefix = "Compiled Kotlin files differ${suffix}:\n " val actualSources = getCompiledKotlinSources(output).projectRelativePaths(this.project) return if (weakTesting) { - assertContainFiles(expectedSources, actualSources, messagePrefix) + assertContainFiles(expectedSourcesRelativePaths, actualSources, messagePrefix) } else { - assertSameFiles(expectedSources, actualSources, messagePrefix) + assertSameFiles(expectedSourcesRelativePaths, actualSources, messagePrefix) } } + fun CompiledProject.assertCompiledKotlinFiles(expectedFiles: Iterable): CompiledProject = + assertCompiledKotlinSources(project.relativize(expectedFiles)) + val Project.allKotlinFiles: Iterable get() = projectDir.allKotlinFiles() diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationBaseIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationBaseIT.kt index 7c3830b75ca..2fa3275047a 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationBaseIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationBaseIT.kt @@ -17,39 +17,46 @@ abstract class IncrementalCompilationBaseIT : BaseGradleIT() { protected fun doTest( fileToModify: String, - modifyFileContents: (String) -> String, - expectedAffectedFileNames: Collection, + modifyFileContents: (originalContents: String) -> String, + expectedCompiledFileNames: Collection, ) { doTest( - { it.projectDir.getFileByName(fileToModify).modify(modifyFileContents) }, - expectedAffectedFileNames + modifyProject = { projectDir.getFileByName(fileToModify).modify(modifyFileContents) }, + expectedCompiledFileNames = expectedCompiledFileNames ) } protected fun doTest( - modifyProject: (Project) -> Unit, - expectedAffectedFileNames: Collection, + project: Project = defaultProject(), + task: String = "build", + options: BuildOptions = defaultBuildOptions(), + modifyProject: Project.() -> Unit, + expectedCompiledFileNames: Collection, ) { - doTest(defaultBuildOptions(), modifyProject, expectedAffectedFileNames) + doTest( + project, task, options, modifyProject, + assertResults = { + assertCompiledKotlinFiles(project.projectDir.getFilesByNames(*expectedCompiledFileNames.toTypedArray())) + } + ) } protected fun doTest( - options: BuildOptions, - modifyProject: (Project) -> Unit, - expectedAffectedFileNames: Collection, + project: Project = defaultProject(), + task: String = "build", + options: BuildOptions = defaultBuildOptions(), + modifyProject: Project.() -> Unit, + assertResults: CompiledProject.() -> Unit ) { - val project = defaultProject() - project.build("build") { + project.build(task, options = options) { assertSuccessful() } modifyProject(project) - project.build("build", options = options) { + project.build(task, options = options) { assertSuccessful() - val expectedAffectedFiles = project.projectDir.getFilesByNames(*expectedAffectedFileNames.toTypedArray()) - val expectedAffectedFileRelativePaths = project.relativize(expectedAffectedFiles) - assertCompiledKotlinSources(expectedAffectedFileRelativePaths) + assertResults() } } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt index f5519f2997c..ea8db1ac95b 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalCompilationMultiProjectIT.kt @@ -1,7 +1,6 @@ package org.jetbrains.kotlin.gradle import org.jetbrains.kotlin.gradle.util.* -import org.junit.Assert import org.junit.Test import java.io.File @@ -91,15 +90,18 @@ open class IncrementalCompilationJvmMultiProjectIT : BaseIncrementalCompilationM assertSuccessful() } - project.projectDir.getFileByName("barUseB.kt").delete() + project.changeMethodBodyInLib() project.build("build") { assertSuccessful() - val affectedSources = File(project.projectDir, "app").allKotlinFiles() - val relativePaths = project.relativize(affectedSources) - assertCompiledKotlinSources(relativePaths) + assertCompiledKotlinFiles(testCompileLibWithGroovy_expectedFiles(project)) } } + /** Expected files to be recompiled for [testCompileLibWithGroovy], which may be overridden in subclasses. */ + open fun testCompileLibWithGroovy_expectedFiles(project: Project): Iterable { + return File(project.projectDir, "app").allKotlinFiles() + File(project.projectDir, "lib").getFileByName("A.kt") + } + /** Regression test for KT-43489. Make sure build history mapping is not initialized too early. */ @Test fun testBuildHistoryMappingLazilyComputedWithWorkers() { @@ -151,67 +153,109 @@ class IncrementalCompilationClasspathSnapshotJvmMultiProjectIT : IncrementalComp abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilationBaseIT() { + protected abstract val compileKotlinTaskName: String + + protected abstract val additionalLibDependencies: String + + protected val changeMethodSignatureInLib: Project.() -> Unit = { + File(projectDir, "lib").getFileByName("A.kt").modify { + it.replace("fun a() {}", "fun a(): Int = 1") + } + } + + protected val changeMethodBodyInLib: Project.() -> Unit = { + File(projectDir, "lib").getFileByName("A.kt").modify { + it.replace("fun a() {}", "fun a() { println() }") + } + } + @Test fun testAbiChangeInLib_changeMethodSignature() { doTest( - "A.kt", - { it.replace("fun a() {}", "fun a(): Int = 1") }, - expectedAffectedFileNames = listOf("A.kt", "B.kt", "AA.kt", "AAA.kt", "BB.kt", "barUseA.kt", "fooUseA.kt") + modifyProject = changeMethodSignatureInLib, + expectedCompiledFileNames = listOf( + "A.kt", "B.kt", "barUseA.kt", // In lib + "AA.kt", "AAA.kt", "BB.kt", "fooUseA.kt" // In app + ) ) } @Test fun testAbiChangeInLib_addNewMethod() { doTest( - "A.kt", - { it.replace("fun a() {}", "fun a() {}\nfun newA() {}") }, - expectedAffectedFileNames = listOf("A.kt", "B.kt", "AA.kt", "AAA.kt", "BB.kt") + modifyProject = { + File(projectDir, "lib").getFileByName("A.kt").modify { + it.replace("fun a() {}", "fun a() {}\nfun newA() {}") + } + }, + expectedCompiledFileNames = listOf( + "A.kt", "B.kt", // In lib + "AA.kt", "AAA.kt", "BB.kt" // In app + ) ) } @Test fun testNonAbiChangeInLib_changeMethodBody() { doTest( - "A.kt", - { it.replace("fun a() {}", "fun a() { println() }") }, - expectedAffectedFileNames = listOf("A.kt") + modifyProject = changeMethodBodyInLib, + expectedCompiledFileNames = listOf("A.kt") // In lib ) } + @Test + fun testAddDependencyInLib() { + doTest( + modifyProject = { + File(projectDir, "lib/build.gradle").modify { + """ + $it + + dependencies { + $additionalLibDependencies + } + """.trimIndent() + } + }, + assertResults = { + assertCompiledKotlinFiles(testAddDependencyInLib_expectedFiles(project)) + } + ) + } + + /** Expected files to be recompiled for [testAddDependencyInLib], which may be overridden in subclasses. */ + open fun testAddDependencyInLib_expectedFiles(project: Project): Iterable { + return File(project.projectDir, "lib").allKotlinFiles() + } + + @Test + fun testAbiChangeInLib_afterLibClean() { // To see if app compilation can be incremental after non-incremental lib compilation + doTest( + modifyProject = { + build(":lib:clean") { assertSuccessful() } + changeMethodSignatureInLib() + }, + assertResults = { + assertCompiledKotlinFiles(testAbiChangeInLib_afterLibClean_expectedFiles(project)) + } + ) + } + + /** Expected files for [testAbiChangeInLib_afterLibClean], which may be overridden in subclasses. */ + open fun testAbiChangeInLib_afterLibClean_expectedFiles(project: Project): Iterable { + return project.projectDir.allKotlinFiles() + } + @Test fun testMoveFunctionFromLibToApp() { doTest( - { project -> - val barUseABKt = project.projectDir.getFileByName("barUseAB.kt") - val barInApp = File(project.projectDir, "app/src/main/kotlin/bar").apply { mkdirs() } + modifyProject = { + val barUseABKt = projectDir.getFileByName("barUseAB.kt") + val barInApp = File(projectDir, "app/src/main/kotlin/bar").apply { mkdirs() } barUseABKt.copyTo(File(barInApp, barUseABKt.name)) barUseABKt.delete() }, - expectedAffectedFileNames = listOf("fooCallUseAB.kt", "barUseAB.kt") - ) - } - - @Test - fun testAddNewMethodToLib() { - doTest( - options = defaultBuildOptions().copy(abiSnapshot = true), - { project -> - val aKt = project.projectDir.getFileByName("A.kt") - aKt.writeText( - """ -package bar - -open class A { - fun a() {} - fun newA() {} -} -""" - ) - }, - //TODO for abi-snapshot "BB.kt" should not be recompiled - expectedAffectedFileNames = listOf( - "A.kt", "B.kt", "AA.kt", "BB.kt", "AAA.kt" - ) + expectedCompiledFileNames = listOf("fooCallUseAB.kt", "barUseAB.kt") ) } @@ -239,139 +283,6 @@ open class A { } } - @Test - fun testCleanBuildLib() { - val project = defaultProject() - - project.build("build") { - assertSuccessful() - } - - project.build(":lib:clean") { - assertSuccessful() - } - - // Change file so Gradle won't skip :app:compile - project.projectFile("BarDummy.kt").modify { - it.replace("class BarDummy", "open class BarDummy") - } - - //don't need to recompile app classes because lib's proto stays the same - project.build("build") { - assertSuccessful() - val affectedSources = project.projectDir.allKotlinFiles() - val relativePaths = project.relativize(affectedSources) - assertCompiledKotlinSources(relativePaths) - } - - val aaKt = project.projectFile("AA.kt") - aaKt.modify { "$it " } - project.build("build") { - assertSuccessful() - assertCompiledKotlinSources(project.relativize(aaKt)) - } - } - - @Test - fun testCleanBuildLibForAbiSnapshot() { - val options = defaultBuildOptions().copy(abiSnapshot = true) - val project = defaultProject() - - project.build("build", options = options) { - assertSuccessful() - } - - project.build(":lib:clean", options = options) { - assertSuccessful() - } - - // Change file so Gradle won't skip :app:compile - project.projectFile("BarDummy.kt").modify { - it.replace("class BarDummy", "open class BarDummy") - } - - //don't need to recompile app classes because lib's proto stays the same - project.build("build", options = options) { - val affectedSources = project.projectDir.allKotlinFiles() - val relativePaths = project.relativize(affectedSources) - assertCompiledKotlinSources(relativePaths) - } - - val aaKt = project.projectFile("AA.kt") - aaKt.modify { "$it " } - project.build("build", options = options) { - assertSuccessful() - assertCompiledKotlinSources(project.relativize(aaKt)) - } - } - - protected abstract val additionalLibDependencies: String - protected abstract val compileKotlinTaskName: String - - @Test - fun testAddMethodToLibForAbiSnapshot() { - val project = defaultProject() - val options = defaultBuildOptions().copy(abiSnapshot = true) - - project.build("build", options = options) { - assertSuccessful() - } - - // Change file so Gradle won't skip :app:compile - project.projectFile("BarDummy.kt").modify { - it.replace("class BarDummy", "open class BarDummy") - } - - val barDummyClassFile = project.projectFile("BarDummy.kt") - barDummyClassFile.modify { "$it { fun m() = 42}" } - - project.build("build", options = options) { - assertSuccessful() - val relativePaths = project.relativize(barDummyClassFile) - assertCompiledKotlinSources(relativePaths) - } - - } - - @Test - fun testAddDependencyToLib() { - val project = defaultProject() - - project.build("build") { - assertSuccessful() - } - - val libBuildGradle = File(project.projectDir, "lib/build.gradle") - Assert.assertTrue("$libBuildGradle does not exist", libBuildGradle.exists()) - libBuildGradle.modify { - """ - $it - - dependencies { - $additionalLibDependencies - } - """.trimIndent() - } - // Change file so Gradle won't skip :app:compile - project.projectFile("BarDummy.kt").modify { - it.replace("class BarDummy", "open class BarDummy") - } - - project.build("build") { - assertSuccessful() - val affectedSources = project.projectDir.allKotlinFiles() - val relativePaths = project.relativize(affectedSources) - assertCompiledKotlinSources(relativePaths) - } - - val aaKt = project.projectFile("AA.kt") - aaKt.modify { "$it " } - project.build("build") { - assertSuccessful() - assertCompiledKotlinSources(project.relativize(aaKt)) - } - } - @Test fun testCompileErrorInLib() { val project = defaultProject() @@ -439,12 +350,14 @@ open class A { fun testMoveFunctionFromLibWithRemappedBuildDirs() { val project = defaultProject() project.setupWorkingDir() - project.projectDir.resolve("build.gradle").appendText(""" + project.projectDir.resolve("build.gradle").appendText( + """ allprojects { it.buildDir = new File(rootDir, "../out" + it.path.replace(":", "/") + "/build") } - """.trimIndent()) + """.trimIndent() + ) project.build("build") { assertSuccessful() } @@ -461,5 +374,49 @@ open class A { assertCompiledKotlinSources(relativePaths) } } -} + @Test + fun testAbiChangeInLib_addNewMethod_withAbiSnapshot() { + doTest( + options = defaultBuildOptions().copy(abiSnapshot = true), + modifyProject = { + File(projectDir, "lib").getFileByName("A.kt").modify { + it.replace("fun a() {}", "fun a() {}\nfun newA() {}") + } + }, + expectedCompiledFileNames = listOf( + "A.kt", "B.kt", // In lib + // TODO(valtman): for abi-snapshot "BB.kt" should not be recompiled + "AA.kt", "AAA.kt", "BB.kt" // In app + ) + ) + } + + @Test + fun testAbiChangeInLib_afterLibClean_withAbiSnapshot() { + doTest( + options = defaultBuildOptions().copy(abiSnapshot = true), + modifyProject = { + build(":lib:clean") { assertSuccessful() } + changeMethodSignatureInLib() + }, + assertResults = { + // TODO: With ABI snapshot, app compilation should be incremental, currently it is not. + assertCompiledKotlinFiles(testAbiChangeInLib_afterLibClean_expectedFiles(project)) + } + ) + } + + @Test + fun testChangeIsolatedClassInLib_withAbiSnapshot() { + doTest( + options = defaultBuildOptions().copy(abiSnapshot = true), + modifyProject = { + File(projectDir, "lib").getFileByName("BarDummy.kt").modify { + "$it { fun m() = 42}" + } + }, + expectedCompiledFileNames = listOf("BarDummy.kt") // In lib + ) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalJavaChangeIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalJavaChangeIT.kt index acbc2f73769..a7a8d6b0a30 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalJavaChangeIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/IncrementalJavaChangeIT.kt @@ -10,12 +10,12 @@ import org.junit.Test open class IncrementalJavaChangeDefaultIT : IncrementalCompilationJavaChangesBase(usePreciseJavaTracking = null) { @Test override fun testAbiChangeInLib_changeMethodSignature_tracked() { - doTest(trackedJavaClass, changeSignature, expectedAffectedFileNames = listOf("TrackedJavaClassChild.kt", "useTrackedJavaClass.kt")) + doTest(trackedJavaClass, changeSignature, expectedCompiledFileNames = listOf("TrackedJavaClassChild.kt", "useTrackedJavaClass.kt")) } @Test override fun testNonAbiChangeInLib_changeMethodBody_tracked() { - doTest(trackedJavaClass, changeBody, expectedAffectedFileNames = listOf()) + doTest(trackedJavaClass, changeBody, expectedCompiledFileNames = listOf()) } } @@ -26,12 +26,12 @@ class IncrementalJavaChangeClasspathSnapshotIT : IncrementalJavaChangeDefaultIT( class IncrementalJavaChangePreciseIT : IncrementalCompilationJavaChangesBase(usePreciseJavaTracking = true) { @Test override fun testAbiChangeInLib_changeMethodSignature_tracked() { - doTest(trackedJavaClass, changeSignature, expectedAffectedFileNames = listOf("TrackedJavaClassChild.kt", "useTrackedJavaClass.kt")) + doTest(trackedJavaClass, changeSignature, expectedCompiledFileNames = listOf("TrackedJavaClassChild.kt", "useTrackedJavaClass.kt")) } @Test override fun testNonAbiChangeInLib_changeMethodBody_tracked() { - doTest(trackedJavaClass, changeBody, expectedAffectedFileNames = listOf()) + doTest(trackedJavaClass, changeBody, expectedCompiledFileNames = listOf()) } } @@ -40,7 +40,7 @@ open class IncrementalJavaChangeDisablePreciseIT : IncrementalCompilationJavaCha override fun testAbiChangeInLib_changeMethodSignature_tracked() { doTest( trackedJavaClass, changeSignature, - expectedAffectedFileNames = listOf( + expectedCompiledFileNames = listOf( "TrackedJavaClassChild.kt", "useTrackedJavaClass.kt", "useTrackedJavaClassFooMethodUsage.kt", "useTrackedJavaClassSameModule.kt" ) @@ -51,7 +51,7 @@ open class IncrementalJavaChangeDisablePreciseIT : IncrementalCompilationJavaCha override fun testNonAbiChangeInLib_changeMethodBody_tracked() { doTest( trackedJavaClass, changeBody, - expectedAffectedFileNames = listOf( + expectedCompiledFileNames = listOf( "TrackedJavaClassChild.kt", "useTrackedJavaClass.kt", "useTrackedJavaClassFooMethodUsage.kt", "useTrackedJavaClassSameModule.kt" ) @@ -77,8 +77,8 @@ abstract class IncrementalCompilationJavaChangesBase(val usePreciseJavaTracking: @Test fun testAbiChangeInLib_changeMethodSignature() { doTest( - javaClass, changeBody, - expectedAffectedFileNames = listOf("JavaClassChild.kt", "useJavaClass.kt", "useJavaClassFooMethodUsage.kt") + javaClass, changeSignature, + expectedCompiledFileNames = listOf("JavaClassChild.kt", "useJavaClass.kt", "useJavaClassFooMethodUsage.kt") ) } @@ -86,7 +86,7 @@ abstract class IncrementalCompilationJavaChangesBase(val usePreciseJavaTracking: fun testNonAbiChangeInLib_changeMethodBody() { doTest( javaClass, changeBody, - expectedAffectedFileNames = listOf("JavaClassChild.kt", "useJavaClass.kt", "useJavaClassFooMethodUsage.kt") + expectedCompiledFileNames = listOf("JavaClassChild.kt", "useJavaClass.kt", "useJavaClassFooMethodUsage.kt") ) }