[Gradle] Improve tests on diagnostics rendering, suppression, failures

This commit is contained in:
Dmitry Savvinov
2023-07-20 10:06:07 +02:00
committed by Space Team
parent a132d37292
commit a9a6441d2d
42 changed files with 500 additions and 7 deletions
@@ -8,6 +8,8 @@ package org.jetbrains.kotlin.gradle.mpp
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.test.TestMetadata
import java.io.File
import kotlin.io.path.appendText
import kotlin.io.path.writeText
@@ -16,9 +18,8 @@ class MppDiagnosticsIt : KGPBaseTest() {
@GradleTest
fun testDiagnosticsRenderingSmoke(gradleVersion: GradleVersion) {
project("diagnosticsRenderingSmoke", gradleVersion) {
val expectedOutputFile = projectPath.resolve("expectedOutput.txt").toFile()
build {
assertEqualsToFile(expectedOutputFile, extractProjectsAndTheirVerboseDiagnostics())
assertEqualsToFile(expectedOutputFile(), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@@ -46,6 +47,132 @@ class MppDiagnosticsIt : KGPBaseTest() {
}
}
@GradleTest
fun testErrorDiagnosticBuildFails(gradleVersion: GradleVersion) {
project("errorDiagnosticBuildFails", gradleVersion) {
// 'assemble' (triggers compileKotlin-tasks indirectly): fail
buildAndFail("assemble") {
assertEqualsToFile(expectedOutputFile("assemble"), extractProjectsAndTheirVerboseDiagnostics())
}
// 'clean', not directly relevant to Kotlin tasks: build is OK
build("clean") {
assertEqualsToFile(expectedOutputFile("clean"), extractProjectsAndTheirVerboseDiagnostics())
}
// Custom task, irrelevant to Kotlin tasks: build is OK
build("myTask", "--rerun-tasks") {
assertEqualsToFile(expectedOutputFile("customTask"), extractProjectsAndTheirVerboseDiagnostics())
}
// commonizer task: build is OK (otherwise IDE will be bricked)
build("commonize") {
assertEqualsToFile(expectedOutputFile("commonize"), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@GradleTest
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_6)
@TestMetadata("errorDiagnosticBuildFails")
fun testErrorDiagnosticBuildFailsWithConfigurationCache(gradleVersion: GradleVersion) {
project("errorDiagnosticBuildFails", gradleVersion) {
buildAndFail("assemble", buildOptions = buildOptions.copy(configurationCache = true)) {
assertConfigurationCacheStored()
assertEqualsToFile(expectedOutputFile("assemble"), extractProjectsAndTheirVerboseDiagnostics())
}
// fails again
buildAndFail("assemble", buildOptions = buildOptions.copy(configurationCache = true)) {
assertConfigurationCacheReused()
assertEqualsToFile(expectedOutputFile("assemble-cache-reused"), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@GradleTest
fun testErrorDiagnosticBuildSucceeds(gradleVersion: GradleVersion) {
project("errorDiagnosticBuildSucceeds", gradleVersion) {
build("assemble") {
assertEqualsToFile(expectedOutputFile("assemble"), extractProjectsAndTheirVerboseDiagnostics())
}
build("myTask", "--rerun-tasks") {
assertEqualsToFile(expectedOutputFile("customTask"), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@GradleTest
fun testSuppressGradlePluginErrors(gradleVersion: GradleVersion) {
project("suppressGradlePluginErrors", gradleVersion) {
// build succeeds
build("assemble") {
assertEqualsToFile(expectedOutputFile(), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@GradleTest
fun testSuppressGradlePluginWarnings(gradleVersion: GradleVersion) {
project("suppressGradlePluginWarnings", gradleVersion) {
build("assemble") {
assertEqualsToFile(expectedOutputFile(), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@GradleTest
fun testSuppressGradlePluginFatals(gradleVersion: GradleVersion) {
project("suppressGradlePluginFatals", gradleVersion) {
buildAndFail("assemble") {
// Gradle 8.0+ for some reason renders exception twice in the build log
val testDataSuffixIfAny = if (gradleVersion < GradleVersion.version(TestVersions.Gradle.G_8_0)) "gradle-6.8.3" else null
assertEqualsToFile(expectedOutputFile(testDataSuffixIfAny), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@GradleTest
fun testErrorsFailOnlyRelevantProjects(gradleVersion: GradleVersion) {
project("errorsFailOnlyRelevantProjects", gradleVersion) {
buildAndFail("brokenProjectA:assemble") {
assertEqualsToFile(expectedOutputFile("brokenA"), extractProjectsAndTheirVerboseDiagnostics())
}
buildAndFail("brokenProjectB:assemble") {
assertEqualsToFile(expectedOutputFile("brokenB"), extractProjectsAndTheirVerboseDiagnostics())
}
build("healthyProject:assemble") {
assertEqualsToFile(expectedOutputFile("healthy"), extractProjectsAndTheirVerboseDiagnostics())
}
// Turn off parallel execution so that order of execution (and therefore the testdata) is stable
buildAndFail("assemble", buildOptions = buildOptions.copy(parallel = false)) {
assertEqualsToFile(expectedOutputFile("root"), extractProjectsAndTheirVerboseDiagnostics())
}
buildAndFail("assemble", "--continue", buildOptions = buildOptions.copy(parallel = false)) {
assertEqualsToFile(expectedOutputFile("root-with-continue"), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
@GradleTest
fun testEarlyTasksMaterializationDoesntBreakReports(gradleVersion: GradleVersion) {
project("earlyTasksMaterializationDoesntBreakReports", gradleVersion) {
buildAndFail("assemble") {
assertEqualsToFile(expectedOutputFile(), extractProjectsAndTheirVerboseDiagnostics())
}
}
}
private fun TestProject.expectedOutputFile(suffix: String? = null): File {
val suffixIfAny = if (suffix != null) "-$suffix" else ""
return projectPath.resolve("expectedOutput$suffixIfAny.txt").toFile()
}
private fun TestProject.checkDeprecatedProperties(isDeprecationExpected: Boolean) {
build {
if (isDeprecationExpected)
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.gradle.testbase
import org.gradle.testkit.runner.BuildResult
import org.jetbrains.kotlin.gradle.BaseGradleIT
import org.jetbrains.kotlin.gradle.internals.ENSURE_NO_KOTLIN_GRADLE_PLUGIN_ERRORS_TASK_NAME
import org.jetbrains.kotlin.gradle.internals.VERBOSE_DIAGNOSTIC_SEPARATOR
import org.jetbrains.kotlin.gradle.plugin.diagnostics.KotlinToolingDiagnostics
import org.jetbrains.kotlin.gradle.plugin.diagnostics.ToolingDiagnosticFactory
@@ -117,11 +118,12 @@ fun BuildResult.extractProjectsAndTheirVerboseDiagnostics(): String = buildStrin
when {
line.trim() == VERBOSE_DIAGNOSTIC_SEPARATOR -> endDiagnostic(line, index)
DIAGNOSTIC_START_REGEX.matches(line) -> startDiagnostic(line, index)
DIAGNOSTIC_START_REGEX.containsMatchIn(line) -> startDiagnostic(line, index)
diagnosticStarted -> continueDiagnostic(line)
line.startsWith(CONFIGURE_PROJECT_PREFIX) -> {
line.startsWith(CONFIGURE_PROJECT_PREFIX)
|| (line.contains(ENSURE_NO_KOTLIN_GRADLE_PLUGIN_ERRORS_TASK_NAME) && line.startsWith(TASK_EXECUTION_PREFIX)) -> {
appendLine() // additional empty line between projects
appendLine(line)
}
@@ -132,9 +134,10 @@ fun BuildResult.extractProjectsAndTheirVerboseDiagnostics(): String = buildStrin
/*
Expected format
w: [DIAGNOSTIC_ID | WARNING] first line of diagnostic's text
or (fatals don't have 'w:' or 'e:' prefix):
[DIAGNOSTIC_ID | FATAL] Fatal diagnostic
*/
private val DIAGNOSTIC_START_REGEX = """\s*[we]:\s*\[[^\[]*].*""".toRegex()
private val DIAGNOSTIC_START_REGEX = """\s*([we]:)?\s*\[\w+ \| \w+].*""".toRegex()
/*
Expected format:
@@ -186,3 +189,4 @@ private fun extractNextVerboselyRenderedDiagnosticAndIndex(
}
private const val CONFIGURE_PROJECT_PREFIX = "> Configure project"
private const val TASK_EXECUTION_PREFIX = "> Task"
@@ -0,0 +1,25 @@
plugins {
kotlin("multiplatform")
}
repositories {
mavenLocal()
mavenCentral()
}
// Materialize tasks early
tasks.all { }
kotlin {
jvm()
linuxX64()
sourceSets {
val myCommonMain by creating
val commonMain by getting {
dependsOn(myCommonMain)
}
}
}
@@ -0,0 +1,8 @@
> Configure project :
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
> Task :ensureNoKotlinGradlePluginErrors FAILED
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
@@ -0,0 +1,2 @@
# We're using dependsOn to provoke CommonMainWithDependsOn error
kotlin.mpp.applyDefaultHierarchyTemplate=false
@@ -0,0 +1,21 @@
plugins {
kotlin("multiplatform")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
// Using deprecated target to provoke ERROR-diagnostic. If you came here because you removed
// that target entirely, just replace it with any other construction from KGP that provokes
// ERROR-diagnostic
@Suppress("DEPRECATION_ERROR")
linuxMips32()
linuxX64()
}
tasks.create("myTask") {
println("Custom Task Executed")
}
@@ -0,0 +1,3 @@
> Task :ensureNoKotlinGradlePluginErrors FAILED
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
@@ -0,0 +1,8 @@
> Configure project :
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
> Task :ensureNoKotlinGradlePluginErrors FAILED
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
@@ -0,0 +1,3 @@
> Configure project :
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
@@ -0,0 +1,3 @@
> Configure project :
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
@@ -0,0 +1,3 @@
> Configure project :
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
@@ -0,0 +1 @@
rootProject.name = "diagnostics-rendering-smoke"
@@ -0,0 +1,17 @@
plugins {
kotlin("multiplatform")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
// no targets declared -- will provoke ERROR-diagnostic, but also no compileKotlin tasks will be created,
// so 'assemble' or similar will pass
}
tasks.create("myTask") {
println("Custom Task Executed")
}
@@ -0,0 +1,11 @@
> Configure project :
e: [NoKotlinTargetsDeclared | ERROR] Please initialize at least one Kotlin target in 'diagnostics-rendering-smoke (:)'.
Read more https://kotl.in/set-up-targets
#diagnostic-end
w: [UnusedSourceSetsWarning | WARNING] The following Kotlin source sets were configured but not added to any Kotlin compilation:
* commonMain
* commonTest
You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotl.in/connecting-source-sets
#diagnostic-end
@@ -0,0 +1,11 @@
> Configure project :
e: [NoKotlinTargetsDeclared | ERROR] Please initialize at least one Kotlin target in 'diagnostics-rendering-smoke (:)'.
Read more https://kotl.in/set-up-targets
#diagnostic-end
w: [UnusedSourceSetsWarning | WARNING] The following Kotlin source sets were configured but not added to any Kotlin compilation:
* commonMain
* commonTest
You can add a source set to a target's compilation by connecting it with the compilation's default source set using 'dependsOn'.
See https://kotl.in/connecting-source-sets
#diagnostic-end
@@ -0,0 +1 @@
rootProject.name = "diagnostics-rendering-smoke"
@@ -0,0 +1,11 @@
plugins {
kotlin("multiplatform")
}
kotlin {
// Using deprecated target to provoke ERROR-diagnostic. If you came here because you removed
// that target entirely, just replace it with any other construction from KGP that provokes
// ERROR-diagnostic
@Suppress("DEPRECATION_ERROR")
linuxMips32()
}
@@ -0,0 +1,16 @@
plugins {
kotlin("multiplatform")
}
kotlin {
jvm()
linuxX64()
sourceSets {
val myCommonMain by creating
val commonMain by getting {
dependsOn(myCommonMain)
}
}
}
@@ -0,0 +1,10 @@
plugins {
kotlin("multiplatform") apply false
}
allprojects {
repositories {
mavenLocal()
mavenCentral()
}
}
@@ -0,0 +1,17 @@
> Configure project :
> Configure project :brokenProjectA
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
> Configure project :brokenProjectB
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
> Configure project :healthyProject
> Task :brokenProjectA:ensureNoKotlinGradlePluginErrors FAILED
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
@@ -0,0 +1,17 @@
> Configure project :
> Configure project :brokenProjectA
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
> Configure project :brokenProjectB
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
> Configure project :healthyProject
> Task :brokenProjectB:ensureNoKotlinGradlePluginErrors FAILED
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
@@ -0,0 +1,15 @@
> Configure project :
> Configure project :brokenProjectA
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
> Configure project :brokenProjectB
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
> Configure project :healthyProject
> Task :healthyProject:ensureNoKotlinGradlePluginErrors
@@ -0,0 +1,25 @@
> Configure project :
> Configure project :brokenProjectA
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
> Configure project :brokenProjectB
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
> Configure project :healthyProject
> Task :brokenProjectA:ensureNoKotlinGradlePluginErrors FAILED
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
> Task :brokenProjectB:ensureNoKotlinGradlePluginErrors FAILED
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
> Task :healthyProject:ensureNoKotlinGradlePluginErrors
@@ -0,0 +1,17 @@
> Configure project :
> Configure project :brokenProjectA
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
> Configure project :brokenProjectB
e: [CommonMainOrTestWithDependsOnDiagnostic | ERROR] commonMain can't declare dependsOn on other source sets
#diagnostic-end
> Configure project :healthyProject
> Task :brokenProjectA:ensureNoKotlinGradlePluginErrors FAILED
e: [DeprecatedKotlinNativeTargetsDiagnostic | ERROR] The following removed Kotlin/Native targets were used in the project: linuxMips32
#diagnostic-end
@@ -0,0 +1,2 @@
# We're using dependsOn in 'brokenProjectB' to provoke CommonMainWithDependsOn error
kotlin.mpp.applyDefaultHierarchyTemplate=false
@@ -0,0 +1,8 @@
plugins {
kotlin("multiplatform")
}
kotlin {
jvm()
linuxX64()
}
@@ -0,0 +1,3 @@
include("brokenProjectA", "brokenProjectB", "healthyProject")
rootProject.name = "diagnostics-rendering-smoke"
@@ -0,0 +1,20 @@
plugins {
kotlin("multiplatform")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
// Using deprecated target to provoke ERROR-diagnostic. If you came here because you removed
// that target entirely, just replace it with any other construction from KGP that provokes
// ERROR-diagnostic
@Suppress("DEPRECATION_ERROR")
linuxMips32()
}
tasks.create("myTask") {
println("Custom Task Executed")
}
@@ -0,0 +1,12 @@
> Configure project :
w: [InternalKotlinGradlePluginPropertiesUsed | WARNING] ATTENTION! This build uses the following Kotlin Gradle Plugin properties:
kotlin.internal.suppressGradlePluginErrors
kotlin.internal.verboseDiagnostics
kotlin.internal-properties are not recommended for production use.
Stability and future compatibility of the build is not guaranteed.
#diagnostic-end
> Task :ensureNoKotlinGradlePluginErrors
@@ -0,0 +1,3 @@
kotlin.internal.suppressGradlePluginErrors=DeprecatedKotlinNativeTargetsDiagnostic
# Check that the warning about internal KGP properties is not suppressible
kotlin.suppressGradlePluginWarning=InternalKotlinGradlePluginPropertiesUsed
@@ -0,0 +1 @@
rootProject.name = "diagnostics-rendering-smoke"
@@ -0,0 +1,22 @@
plugins {
kotlin("multiplatform")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
jvm()
sourceSets {
val intermediate by creating
val jvmMain by getting {
dependsOn(intermediate)
}
intermediate.dependsOn(jvmMain)
}
}
@@ -0,0 +1,6 @@
> Configure project :
[CircularDependsOnEdges | FATAL] Circular dependsOn hierarchy found in the Kotlin source sets: intermediate -> jvmMain -> intermediate
#diagnostic-end
org.gradle.api.InvalidUserCodeException: [CircularDependsOnEdges | FATAL] Circular dependsOn hierarchy found in the Kotlin source sets: intermediate -> jvmMain -> intermediate
#diagnostic-end
@@ -0,0 +1,12 @@
> Configure project :
[CircularDependsOnEdges | FATAL] Circular dependsOn hierarchy found in the Kotlin source sets: intermediate -> jvmMain -> intermediate
#diagnostic-end
org.gradle.api.InvalidUserCodeException: [CircularDependsOnEdges | FATAL] Circular dependsOn hierarchy found in the Kotlin source sets: intermediate -> jvmMain -> intermediate
#diagnostic-end
[CircularDependsOnEdges | FATAL] Circular dependsOn hierarchy found in the Kotlin source sets: intermediate -> jvmMain -> intermediate
#diagnostic-end
org.gradle.api.InvalidUserCodeException: [CircularDependsOnEdges | FATAL] Circular dependsOn hierarchy found in the Kotlin source sets: intermediate -> jvmMain -> intermediate
#diagnostic-end
@@ -0,0 +1,2 @@
kotlin.internal.suppressGradlePluginErrors=CircularDependsOnEdges
kotlin.suppressGradlePluginWarnings=CircularDependsOnEdges
@@ -0,0 +1 @@
rootProject.name = "diagnostics-rendering-smoke"
@@ -0,0 +1,16 @@
plugins {
kotlin("multiplatform")
}
repositories {
mavenLocal()
mavenCentral()
}
kotlin {
jvm()
sourceSets {
val orphan by creating { }
}
}
@@ -0,0 +1,3 @@
> Configure project :
> Task :ensureNoKotlinGradlePluginErrors
@@ -0,0 +1 @@
kotlin.suppressGradlePluginWarnings=UnusedSourceSetsWarning
@@ -0,0 +1 @@
rootProject.name = "suppress-gradle-plugin-warnings"
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.gradle.internals
import org.jetbrains.kotlin.compilerRunner.asFinishLogMessage
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.PropertyNames.KOTLIN_NATIVE_IGNORE_DISABLED_TARGETS
import org.jetbrains.kotlin.gradle.plugin.diagnostics.DIAGNOSTIC_SEPARATOR
import org.jetbrains.kotlin.gradle.plugin.diagnostics.EnsureNoKotlinGradlePluginErrors
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinProjectStructureMetadata
import org.jetbrains.kotlin.gradle.plugin.mpp.MULTIPLATFORM_PROJECT_METADATA_JSON_FILE_NAME
import org.jetbrains.kotlin.gradle.plugin.mpp.parseKotlinSourceSetMetadataFromJson
@@ -21,5 +22,7 @@ const val KOTLIN_NATIVE_IGNORE_DISABLED_TARGETS_PROPERTY = KOTLIN_NATIVE_IGNORE_
const val VERBOSE_DIAGNOSTIC_SEPARATOR = DIAGNOSTIC_SEPARATOR
const val ENSURE_NO_KOTLIN_GRADLE_PLUGIN_ERRORS_TASK_NAME = EnsureNoKotlinGradlePluginErrors.TASK_NAME
val KotlinCompilerExecutionStrategy.asFinishLogMessage: String
get() = this.asFinishLogMessage
get() = this.asFinishLogMessage