diff --git a/libraries/tools/kotlin-gradle-plugin-idea-for-compatibility-tests/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-idea-for-compatibility-tests/build.gradle.kts index d08c5a4f742..802c83dfa2f 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea-for-compatibility-tests/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-idea-for-compatibility-tests/build.gradle.kts @@ -4,7 +4,7 @@ * Version of kotlin-gradle-plugin-idea module that should be resolved for compatibility tests * This version can be treated as 'minimal guaranteed backwards compatible version' of the module. */ -val testedVersion = "1.8.20-dev-2192" +val testedVersion = "1.8.20-dev-2237" val isSnapshotTest = properties.contains("kgp-idea.snapshot_test") val resolvedTestedVersion = if (isSnapshotTest) properties["defaultSnapshotVersion"].toString() else testedVersion @@ -12,16 +12,13 @@ val resolvedTestedVersion = if (isSnapshotTest) properties["defaultSnapshotVersi //region Download and prepare classpath for specified tested version repositories { - maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") - maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide-plugin-dependencies") -} - -if (isSnapshotTest) { - repositories { - clear() + if (isSnapshotTest) { mavenLocal() mavenCentral() } + + maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") + maven(url = "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide-plugin-dependencies") } val classpathDestination = layout.buildDirectory.dir("classpath") diff --git a/libraries/tools/kotlin-gradle-plugin-idea-proto/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-idea-proto/build.gradle.kts index 0ceac70f23c..45ccebc908c 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea-proto/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-idea-proto/build.gradle.kts @@ -58,6 +58,7 @@ run { artifacts.add(binaryValidationApiElements.name, binaryValidationApiJar) } +/* Setup protoc */ tasks.register("protoc") { val protoSources = file("src/main/proto") val javaOutput = file("src/generated/java/") @@ -87,3 +88,25 @@ tasks.register("protoc") { .map { it.path }, ) } + + +/* Setup backwards compatibility tests */ +run { + val compatibilityTestClasspath by configurations.creating { + isCanBeResolved = true + isCanBeConsumed = false + attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME)) + attributes.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY)) + } + + dependencies { + compatibilityTestClasspath(project(":kotlin-gradle-plugin-idea-for-compatibility-tests")) + } + + tasks.test { + dependsOn(compatibilityTestClasspath) + inputs.files(compatibilityTestClasspath) + doFirst { systemProperty("compatibilityTestClasspath", compatibilityTestClasspath.files.joinToString(";") { it.absolutePath }) } + } +} + diff --git a/libraries/tools/kotlin-gradle-plugin-idea-proto/src/test/kotlin/org/jetbrains/kotlin/gradle/idea/proto/backwardsCompatibilityTestUtils.kt b/libraries/tools/kotlin-gradle-plugin-idea-proto/src/test/kotlin/org/jetbrains/kotlin/gradle/idea/proto/backwardsCompatibilityTestUtils.kt new file mode 100644 index 00000000000..3cf040bbf7f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea-proto/src/test/kotlin/org/jetbrains/kotlin/gradle/idea/proto/backwardsCompatibilityTestUtils.kt @@ -0,0 +1,23 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.idea.proto + +import java.io.File +import java.net.URLClassLoader + +fun classLoaderForBackwardsCompatibleClasses(): ClassLoader { + val uris = classpathForBackwardsCompatibleClasses().map { file -> file.toURI().toURL() }.toTypedArray() + return URLClassLoader.newInstance(uris, null) +} + +fun classpathForBackwardsCompatibleClasses(): List { + val compatibilityTestClasspath = System.getProperty("compatibilityTestClasspath") + ?: error("Missing compatibilityTestClasspath system property") + + return compatibilityTestClasspath.split(";").map { path -> File(path) } + .onEach { file -> if (!file.exists()) println("[WARNING] Missing $file") } + .flatMap { file -> if (file.isDirectory) file.listFiles().orEmpty().toList() else listOf(file) } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea-proto/src/test/kotlin/org/jetbrains/kotlin/gradle/idea/proto/tcs/IdeaKotlinDependencyBackwardsCompatibilityTest.kt b/libraries/tools/kotlin-gradle-plugin-idea-proto/src/test/kotlin/org/jetbrains/kotlin/gradle/idea/proto/tcs/IdeaKotlinDependencyBackwardsCompatibilityTest.kt new file mode 100644 index 00000000000..0e454cfc2e7 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea-proto/src/test/kotlin/org/jetbrains/kotlin/gradle/idea/proto/tcs/IdeaKotlinDependencyBackwardsCompatibilityTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.gradle.idea.proto.tcs + +import org.jetbrains.kotlin.gradle.idea.proto.classLoaderForBackwardsCompatibleClasses +import org.jetbrains.kotlin.gradle.idea.serialize.IdeaKotlinSerializationLogger +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinResolvedBinaryDependency +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinSourceDependency +import org.jetbrains.kotlin.gradle.idea.tcs.IdeaKotlinUnresolvedBinaryDependency +import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.TestIdeaKotlinDependencySerializer +import org.jetbrains.kotlin.gradle.idea.testFixtures.tcs.TestIdeaKotlinInstances +import org.jetbrains.kotlin.gradle.idea.testFixtures.utils.copy +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertSame + +class IdeaKotlinDependencyBackwardsCompatibilityTest { + + @Test + fun `test - simple unresolved binary dependency`() { + val dependency = TestIdeaKotlinInstances.simpleUnresolvedBinaryDependency + val binary = TestIdeaKotlinDependencySerializer().serialize(dependency) + val deserialized = deserializeIdeaKotlinDependencyWithBackwardsCompatibleClasses(binary) + val deserializedCopied = deserialized.copy() + + assertEquals(dependency.cause, deserializedCopied.cause) + assertEquals(dependency.coordinates, deserializedCopied.coordinates) + assertEquals(dependency.extras, deserializedCopied.extras) + } + + @Test + fun `test - simple resolved binary dependency`() { + val dependency = TestIdeaKotlinInstances.simpleResolvedBinaryDependency + val binary = TestIdeaKotlinDependencySerializer().serialize(dependency) + val deserialized = deserializeIdeaKotlinDependencyWithBackwardsCompatibleClasses(binary) + val deserializedCopied = deserialized.copy() + + assertEquals(dependency.coordinates, deserializedCopied.coordinates) + assertEquals(dependency.binaryType, deserializedCopied.binaryType) + assertEquals(dependency.binaryFile, deserializedCopied.binaryFile) + assertEquals(dependency.extras, deserializedCopied.extras) + } + + @Test + fun `test - simple source dependency`() { + val dependency = TestIdeaKotlinInstances.simpleSourceDependency + val binary = TestIdeaKotlinDependencySerializer().serialize(dependency) + val deserialized = deserializeIdeaKotlinDependencyWithBackwardsCompatibleClasses(binary) + val deserializedCopied = deserialized.copy() + + assertEquals(dependency.type, deserializedCopied.type) + assertEquals(dependency.coordinates, deserializedCopied.coordinates) + assertEquals(dependency.extras, deserializedCopied.extras) + } +} + +private fun deserializeIdeaKotlinDependencyWithBackwardsCompatibleClasses(project: ByteArray): Any { + val classLoader = classLoaderForBackwardsCompatibleClasses() + val serializer = TestIdeaKotlinDependencySerializer(classLoader) + + val deserialized = assertNotNull( + serializer.deserialize(project), + "Failed to deserialize dependency: ${serializer.reports}" + ) + + assertEquals( + 0, serializer.reports.count { it.severity > IdeaKotlinSerializationLogger.Severity.WARNING }, + "Expected no severe deserialization reports. Found ${serializer.reports}" + ) + + assertSame( + classLoader, deserialized::class.java.classLoader, + "Expected model do be deserialized in with old classes" + ) + + return deserialized +}