KT-45777: Refactor incremental compilation integration tests

to organize tested scenarios better and make it easier to see the
effects of the next change when the classpath snapshot feature is
enabled.
This commit is contained in:
Hung Nguyen
2021-08-25 09:47:37 +01:00
committed by nataliya.valtman
parent 3f857cdef1
commit e3d7b7a30e
4 changed files with 176 additions and 207 deletions
@@ -122,7 +122,7 @@ abstract class BaseGradleIT {
// gradle wrapper version to wrapper directory
private val gradleWrappers = hashMapOf<String, File>()
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<String>,
expectedSourcesRelativePaths: Iterable<String>,
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<File>): CompiledProject =
assertCompiledKotlinSources(project.relativize(expectedFiles))
val Project.allKotlinFiles: Iterable<File>
get() = projectDir.allKotlinFiles()
@@ -17,39 +17,46 @@ abstract class IncrementalCompilationBaseIT : BaseGradleIT() {
protected fun doTest(
fileToModify: String,
modifyFileContents: (String) -> String,
expectedAffectedFileNames: Collection<String>,
modifyFileContents: (originalContents: String) -> String,
expectedCompiledFileNames: Collection<String>,
) {
doTest(
{ it.projectDir.getFileByName(fileToModify).modify(modifyFileContents) },
expectedAffectedFileNames
modifyProject = { projectDir.getFileByName(fileToModify).modify(modifyFileContents) },
expectedCompiledFileNames = expectedCompiledFileNames
)
}
protected fun doTest(
modifyProject: (Project) -> Unit,
expectedAffectedFileNames: Collection<String>,
project: Project = defaultProject(),
task: String = "build",
options: BuildOptions = defaultBuildOptions(),
modifyProject: Project.() -> Unit,
expectedCompiledFileNames: Collection<String>,
) {
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<String>,
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()
}
}
}
@@ -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<File> {
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<File> {
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<File> {
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
)
}
}
@@ -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")
)
}