From 0c5eda13823db65a9e8eb95d9b7f9f07c5c66052 Mon Sep 17 00:00:00 2001 From: Hung Nguyen Date: Wed, 20 Jul 2022 10:48:51 +0100 Subject: [PATCH] Revise incremental compilation failure handling tests Revise BaseIncrementalCompilationMultiProjectIT's failure handling tests to make it clear we have covered 2 important scenarios: - Failures caused by user errors: testFailureHandling_UserError - Failures caused by tool errors: testFailureHandling_ToolError ^KT-53231 In progress --- .../kotlin/gradle/BuildCacheRelocationIT.kt | 4 +- .../IncrementalCompilationMultiProjectIT.kt | 146 ++++++++---------- .../gradle/testbase/compilationAssertions.kt | 4 +- 3 files changed, 66 insertions(+), 88 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt index 4d280e8a256..c05459472d5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildCacheRelocationIT.kt @@ -335,7 +335,7 @@ class BuildCacheRelocationIT : KGPBaseTest() { @GradleTest fun testKotlinIncrementalCompilation_withGradleClasspathSnapshot(gradleVersion: GradleVersion) { checkKotlinIncrementalCompilationAfterCacheHit(gradleVersion, defaultBuildOptions.copy(useGradleClasspathSnapshot = true)) { - assertIncrementalCompilation(listOf("src/main/kotlin/foo.kt", "src/main/kotlin/fooUsage.kt")) + assertIncrementalCompilation(listOf("src/main/kotlin/foo.kt", "src/main/kotlin/fooUsage.kt").toPaths()) } } @@ -343,7 +343,7 @@ class BuildCacheRelocationIT : KGPBaseTest() { @GradleTest fun testKotlinIncrementalCompilation_withICClasspathSnapshot(gradleVersion: GradleVersion) { checkKotlinIncrementalCompilationAfterCacheHit(gradleVersion, defaultBuildOptions.copy(useICClasspathSnapshot = true)) { - assertIncrementalCompilation(listOf("src/main/kotlin/foo.kt", "src/main/kotlin/fooUsage.kt")) + assertIncrementalCompilation(listOf("src/main/kotlin/foo.kt", "src/main/kotlin/fooUsage.kt").toPaths()) assertOutputContains("Incremental compilation with ABI snapshot enabled") } } 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 3adbdbe701c..36d34e0fde8 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 @@ -2,9 +2,9 @@ package org.jetbrains.kotlin.gradle import org.gradle.testkit.runner.BuildResult import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.build.report.metrics.BuildAttribute import org.jetbrains.kotlin.gradle.testbase.* import org.jetbrains.kotlin.gradle.util.checkedReplace -import org.jetbrains.kotlin.test.KtAssert.assertTrue import org.junit.jupiter.api.DisplayName import java.nio.file.Path import kotlin.io.path.* @@ -38,8 +38,7 @@ class IncrementalCompilationJsMultiProjectIT : BaseIncrementalCompilationMultiPr get() = "caches-js" //compileKotlin2Js's modification doe not work - override fun testIncrementalBuildWithCompilationError(gradleVersion: GradleVersion) {} - override fun testValidOutputsWithCacheCorrupted(gradleVersion: GradleVersion) {} + override fun testFailureHandling_ToolError(gradleVersion: GradleVersion) {} } @JvmGradlePluginTests @@ -502,44 +501,6 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation } } - @DisplayName("compile error in lib project") - @GradleTest - fun testCompileErrorInLib(gradleVersion: GradleVersion) { - defaultProject(gradleVersion) { - build("assemble") - - val bKt = subProject("lib").kotlinSourcesDir().resolve("bar/B.kt") - val bKtContent = bKt.readText() - bKt.deleteExisting() - - fun runFailingBuild() { - buildAndFail("assemble") { - assertOutputContains("B.kt has been removed") - assertTasksFailed(":lib:$compileKotlinTaskName") - assertCompiledKotlinSources( - getExpectedKotlinSourcesForDefaultProject( - libSources = listOf("bar/barUseAB.kt", "bar/barUseB.kt") - ), - output - ) - } - } - - runFailingBuild() - runFailingBuild() - - bKt.writeText(bKtContent.replace("fun b", "open fun b")) - - build("assemble") { - val expectedSources = getExpectedKotlinSourcesForDefaultProject( - libSources = listOf("bar/B.kt", "bar/barUseAB.kt", "bar/barUseB.kt"), - appSources = listOf("foo/BB.kt", "foo/fooUseB.kt") - ) - assertCompiledKotlinSources(expectedSources, output) - } - } - } - @DisplayName("Remove library from classpath") @GradleTest fun testRemoveLibFromClasspath(gradleVersion: GradleVersion) { @@ -687,63 +648,80 @@ abstract class BaseIncrementalCompilationMultiProjectIT : IncrementalCompilation } } - @DisplayName("KT-49780: Valid outputs after cache corruption exception") + @DisplayName("Test handling of failures caused by user errors") @GradleTest - open fun testValidOutputsWithCacheCorrupted(gradleVersion: GradleVersion) { + fun testFailureHandling_UserError(gradleVersion: GradleVersion) { defaultProject(gradleVersion) { - breakCachesAfterCompileKotlinExecution(this) + // Perform the first non-incremental build + build(":lib:compileKotlin") - build("assemble") - - subProject("lib").kotlinSourcesDir().resolve("bar/B.kt").modify { - it.replace("fun b() {}", "fun b() = 123") + // Make a compile error in the source code + var classAKtContents: String? = null + subProject("lib").kotlinSourcesDir().resolve("bar/A.kt").modify { + classAKtContents = it + it.replace("fun a() {}", "fun a() { // Compile error: Missing closing bracket") } - build("assemble") { - assertOutputContains("Non-incremental compilation will be performed: INCREMENTAL_COMPILATION_FAILED") + // In the next build, compilation should be incremental and fail, and not fall back to non-incremental compilation + buildAndFail(":lib:compileKotlin") { + assertTasksFailed(":lib:$compileKotlinTaskName") + assertOutputContains("Compilation error. See log for more details") + assertOutputDoesNotContain("Non-incremental compilation will be performed: ${BuildAttribute.INCREMENTAL_COMPILATION_FAILED.name}") } - val lookupFile = projectPath.resolve("lib/build/kotlin/${compileKotlinTaskName}/cacheable/${compileCacheFolderName}/lookups/file-to-id.tab") - assertTrue("Output is empty", lookupFile.exists()) + // Fix the compile error in the source code + subProject("lib").kotlinSourcesDir().resolve("bar/A.kt").writeText(classAKtContents!!) + changeMethodSignatureInLib() - } - } - - @DisplayName("KT-49780: No need to rebuild invalid code due to cache corruption") - @GradleTest - open fun testIncrementalBuildWithCompilationError(gradleVersion: GradleVersion) { - defaultProject(gradleVersion) { - breakCachesAfterCompileKotlinExecution(this) - - build("assemble") - - subProject("lib").kotlinSourcesDir().resolve("bar/B.kt").modify { - it.replace("fun b() {}", "fun a() = 123") - } - - buildAndFail("assemble") { - printBuildOutput() - assertOutputDoesNotContain("Possible caches corruption:") - assertOutputDoesNotContain("Non-incremental compilation will be performed:") + // In the next build, compilation should be incremental and succeed + build(":lib:compileKotlin") { + assertIncrementalCompilation( + expectedCompiledKotlinFiles = getExpectedKotlinSourcesForDefaultProject( + libSources = listOf("bar/A.kt", "bar/B.kt", "bar/barUseA.kt") + ) + ) } } } - private fun breakCachesAfterCompileKotlinExecution(testProject: TestProject) { - listOf("app", "lib").forEach { - testProject.subProject(it).buildGradle.appendText( - """ - $compileKotlinTaskName { - doLast { - def file = new File(projectDir.path, "/build/kotlin/${compileKotlinTaskName}/cacheable/${compileCacheFolderName}/lookups/file-to-id.tab") - println("Update lookup file " + file.path) - file.write("la-la") - } - } - """.trimIndent() - ) + @DisplayName("Test handling of failures caused by tool errors") + @GradleTest + open fun testFailureHandling_ToolError(gradleVersion: GradleVersion) { + defaultProject(gradleVersion) { + // Simulate a tool error by registering a doLast that breaks caches in the KotlinCompile task + val lookupFile = + projectPath.resolve("lib/build/kotlin/${compileKotlinTaskName}/cacheable/${compileCacheFolderName}/lookups/file-to-id.tab") + breakCachesAfterKotlinCompile(subProject("lib"), lookupFile) + + // Perform the first non-incremental build + build(":lib:compileKotlin") { + // Caches should be in a corrupted state, which will ensure the next build will fail + assertFileContains(lookupFile, "Invalid contents") + } + + // Make a change in the source code + changeMethodSignatureInLib() + + // In the next build, compilation should be incremental and fail, then fall back to non-incremental compilation and succeed + build(":lib:compileKotlin") { + assertOutputContains("Non-incremental compilation will be performed: ${BuildAttribute.INCREMENTAL_COMPILATION_FAILED.name}") + // Also check that the output is not deleted (regression test for KT-49780) + assertFileExists(lookupFile) + } } } + private fun breakCachesAfterKotlinCompile(gradleProject: GradleProject, lookupFile: Path) { + gradleProject.buildGradle.appendText( + """ + $compileKotlinTaskName { + doLast { + new File("$lookupFile").write("Invalid contents") + } + } + """.trimIndent() + ) + } + } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt index ddb93638189..f66dc751f23 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/compilationAssertions.kt @@ -101,9 +101,9 @@ fun BuildResult.assertNonIncrementalCompilation() { * * Note: Log level of output must be set to [LogLevel.DEBUG]. */ -fun BuildResult.assertIncrementalCompilation(expectedCompiledKotlinFiles: Iterable) { +fun BuildResult.assertIncrementalCompilation(expectedCompiledKotlinFiles: Iterable) { assertOutputDoesNotContain("Non-incremental compilation will be performed") val actualCompiledKotlinFiles = extractCompiledKotlinFiles(output) - assertSameFiles(expectedCompiledKotlinFiles.toPaths(), actualCompiledKotlinFiles, "Compiled Kotlin files differ:\n") + assertSameFiles(expectedCompiledKotlinFiles, actualCompiledKotlinFiles, "Compiled Kotlin files differ:\n") }