Downgrade -api-version in test compilations

Since 2.2.20 uncaught exceptions in coroutines make the Test process
hang if the stdlib is downgraded without appropriate -api-version
downgrade.

^KT-79528
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
index cdeb33a..c4f387d 100644
--- a/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/build.gradle.kts
@@ -1,6 +1,7 @@
 import org.gradle.api.tasks.testing.logging.TestExceptionFormat
 import org.jetbrains.kotlin.build.androidsdkprovisioner.ProvisioningType
 import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
 import org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile
 import java.nio.file.Paths
 
@@ -26,6 +27,9 @@
             "-Xlambdas=class"
         )
     }
+
+    @Suppress("DEPRECATION")
+    target.compilations.getByName("test").downgradeApiVersionToAvoidBreakingCoroutines(KotlinVersion.KOTLIN_2_0)
 }
 
 tasks.withType(AbstractKotlinCompile::class.java).configureEach {
diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KT79528StdlibDowngradeHangs.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KT79528StdlibDowngradeHangs.kt
new file mode 100644
index 0000000..a5ba7bf
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/KT79528StdlibDowngradeHangs.kt
@@ -0,0 +1,22 @@
+package org.jetbrains.kotlin.gradle
+
+import kotlin.test.Test
+import kotlinx.coroutines.async
+import kotlinx.coroutines.runBlocking
+import org.jetbrains.kotlin.util.assertThrows
+
+class KT79528StdlibDowngradeHangs {
+
+    @Test
+    fun `test KT-79528 - test Gradle doesn't downgrade stdlib and cause uncaught exceptions in coroutines to hang coroutines machinery`() {
+        class Foo : RuntimeException()
+        assertThrows<Foo> {
+            runBlocking {
+                async {
+                    throw Foo()
+                }.await()
+            }
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts
index 2526bf2..6ae3dd5 100644
--- a/libraries/tools/kotlin-gradle-plugin/build.gradle.kts
+++ b/libraries/tools/kotlin-gradle-plugin/build.gradle.kts
@@ -1,6 +1,7 @@
 import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
 import gradle.GradlePluginVariant
 import org.jetbrains.kotlin.build.androidsdkprovisioner.ProvisioningType
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 
 plugins {
@@ -32,6 +33,8 @@
             )
         )
     }
+
+    target.compilations.getByName("test").downgradeApiVersionToAvoidBreakingCoroutines()
 }
 
 tasks.test {
@@ -539,6 +542,7 @@
             kotlinJavaToolchain.toolchain.use(project.getToolchainLauncherFor(JdkMajorVersion.JDK_17_0))
         }
     }
+    functionalTestCompilation.downgradeApiVersionToAvoidBreakingCoroutines()
 
     functionalTestCompilation.configurations.pluginConfiguration.dependencies.add(
         dependencies.create("org.jetbrains.kotlin:kotlin-serialization-compiler-plugin-embeddable")
diff --git a/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KT79528StdlibDowngradeHangs.kt b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KT79528StdlibDowngradeHangs.kt
new file mode 100644
index 0000000..6d0e63a
--- /dev/null
+++ b/libraries/tools/kotlin-gradle-plugin/src/functionalTest/kotlin/org/jetbrains/kotlin/gradle/unitTests/KT79528StdlibDowngradeHangs.kt
@@ -0,0 +1,21 @@
+package org.jetbrains.kotlin.gradle.unitTests
+
+import kotlin.test.Test
+import kotlinx.coroutines.async
+import kotlinx.coroutines.runBlocking
+import org.jetbrains.kotlin.util.assertThrows
+
+class KT79528StdlibDowngradeHangs {
+
+    @Test
+    fun `test KT-79528 - test Gradle doesn't downgrade stdlib and cause uncaught exceptions in coroutines to hang coroutines machinery`() {
+        class Foo : RuntimeException()
+        assertThrows<Foo> {
+            runBlocking {
+                async {
+                    throw Foo()
+                }.await()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/GradleCommon.kt b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/GradleCommon.kt
index cf47269..6064406 100644
--- a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/GradleCommon.kt
+++ b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/GradleCommon.kt
@@ -10,6 +10,7 @@
 import org.gradle.api.GradleException
 import org.gradle.api.Project
 import org.gradle.api.artifacts.Configuration
+import org.gradle.api.artifacts.FileCollectionDependency
 import org.gradle.api.artifacts.ModuleDependency
 import org.gradle.api.artifacts.VersionCatalogsExtension
 import org.gradle.api.artifacts.type.ArtifactTypeDefinition
@@ -43,10 +44,12 @@
 import org.jetbrains.kotlin.buildtools.api.ExperimentalBuildToolsApi
 import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
 import org.jetbrains.kotlin.gradle.dsl.KotlinBaseExtension
+import org.jetbrains.kotlin.gradle.dsl.KotlinJvmExtension
 import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension
 import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
 import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
 import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
+import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinWithJavaCompilation
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
 import plugins.configureDefaultPublishing
@@ -808,4 +811,35 @@
                 }
             }
         }
+}
+
+fun Project.checkTestCompilationsLowerApiVersionIfEmbeddedStdlibIsInRuntimeClasspath() {
+    extensions.getByType<KotlinJvmExtension>().target.compilations.matching {
+        it.name.lowercase().contains("test")
+    }.all {
+        val compilation = this
+        compilation.compileTaskProvider.configure {
+            val task = this
+            val gradleEmbeddedStdlib =
+                (dependencies.gradleApi() as FileCollectionDependency).files.single { it.name.startsWith("kotlin-stdlib") }
+            val willCoroutinesHangOnUncaughtExceptions: Provider<Boolean> = provider {
+                val runtimeExportsEmbeddedStdlib = compilation.runtimeDependencyFiles?.contains(gradleEmbeddedStdlib)
+                    ?: error("Missing runtime dependency files")
+                val v2DebugMetadataIsEmitted = task.compilerOptions.apiVersion.get() >= KotlinVersion.KOTLIN_2_3
+                runtimeExportsEmbeddedStdlib && v2DebugMetadataIsEmitted
+            }
+            doFirst {
+                if (willCoroutinesHangOnUncaughtExceptions.get()) {
+                    error("""
+                        Test compilation task '${name}' depends on Gradle dependency with embedded kotlin-stdlib version, but doesn't lower -api-version. 
+                        Please remove Gradle's stdlib from runtime classpath or downgrade -api-version to ${KotlinVersion.KOTLIN_2_2} or lower using downgradeApiVersionToAvoidBreakingCoroutines()
+                    """.trimIndent())
+                }
+            }
+        }
+    }
+}
+
+fun KotlinWithJavaCompilation<*, *>.downgradeApiVersionToAvoidBreakingCoroutines(apiVersion: KotlinVersion = KotlinVersion.KOTLIN_2_2) {
+    compileTaskProvider.configure { compilerOptions.apiVersion.set(apiVersion) }
 }
\ No newline at end of file
diff --git a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts
index 7a7c5ba..7ab2f69 100644
--- a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts
+++ b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-common-configuration.gradle.kts
@@ -123,3 +123,5 @@
     )
     publishShadowedJar(gradle813SourceSet, commonSourceSet)
 }
+
+checkTestCompilationsLowerApiVersionIfEmbeddedStdlibIsInRuntimeClasspath()
\ No newline at end of file
diff --git a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts
index 83e3cff..2e59994 100644
--- a/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts
+++ b/repo/gradle-build-conventions/buildsrc-compat/src/main/kotlin/gradle-plugin-dependency-configuration.gradle.kts
@@ -86,3 +86,4 @@
     }
 }
 
+checkTestCompilationsLowerApiVersionIfEmbeddedStdlibIsInRuntimeClasspath()
\ No newline at end of file