From 55448943af19059c768ed4bf9fe7ec83a4ceef39 Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Sun, 13 Feb 2022 13:05:43 +0100 Subject: [PATCH] [KPM] Implement BackwardsCompatibilityDeserializationTest KT-51262 --- .../build.gradle.kts | 29 ++++- ...ckwardsCompatibilityDeserializationTest.kt | 109 ++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt diff --git a/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts b/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts index 9ec51fa7b0b..0f3eafc6432 100644 --- a/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts +++ b/libraries/tools/kotlin-gradle-plugin-idea/build.gradle.kts @@ -1,9 +1,36 @@ plugins { - id("gradle-plugin-common-configuration") + kotlin("jvm") +} + +kotlin.sourceSets.configureEach { + languageSettings.apiVersion = "1.4" + languageSettings.languageVersion = "1.4" } dependencies { implementation(kotlinStdlib()) + testImplementation(gradleApi()) + testImplementation(gradleKotlinDsl()) + testImplementation(project(":kotlin-gradle-plugin")) + testImplementation(project(":kotlin-test:kotlin-test-junit")) } publish() + +tasks.test { + /* Setup for backwards compatibility test */ + val minimalBackwardsCompatibleVersion = "1.7.0-dev-1825" + val minimalBackwardsCompatibleVersionTestClasspath by configurations.creating + dependencies { + minimalBackwardsCompatibleVersionTestClasspath("org.jetbrains.kotlin:kotlin-gradle-plugin-idea:$minimalBackwardsCompatibleVersion") + } + + dependsOn(minimalBackwardsCompatibleVersionTestClasspath) + + doFirst { + systemProperty( + "backwardsCompatibilityClasspath", + minimalBackwardsCompatibleVersionTestClasspath.files.joinToString(";") { it.absolutePath } + ) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt new file mode 100644 index 00000000000..390162de7ba --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/test/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/BackwardsCompatibilityDeserializationTest.kt @@ -0,0 +1,109 @@ +/* + * 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. + */ + +@file:OptIn(InternalKotlinGradlePluginApi::class) + +package org.jetbrains.kotlin.gradle.kpm.idea + +import org.gradle.api.Project +import org.gradle.api.internal.project.ProjectInternal +import org.gradle.configurationcache.extensions.serviceOf +import org.gradle.kotlin.dsl.create +import org.gradle.testfixtures.ProjectBuilder +import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry +import org.jetbrains.kotlin.gradle.plugin.KotlinPm20PluginWrapper +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinIosX64Variant +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinLinuxX64Variant +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension +import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.jvm +import org.junit.Test +import java.io.* +import java.net.URLClassLoader +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertSame + +/** + * This test is designed to test if serialized models can still be deserialized with a older (minimal supported) + * version of this module. + * + * The test will + * - Build a representative 'sample' model + * - Serialize this model + * - Deserialize this model with the older version of the classes (using a custom class loader) + * - Assert the deserialized model is healthy + */ +class BackwardsCompatibilityDeserializationTest { + + @Test + fun `test - simple project`() { + val project = ProjectBuilder.builder().build() as ProjectInternal + project.plugins.apply(KotlinPm20PluginWrapper::class.java) + + /* Setup example project */ + val kotlinExtension = project.extensions.getByType(KotlinPm20ProjectExtension::class.java) + kotlinExtension.mainAndTest { + jvm + val native = fragments.create("native") + val linux = fragments.create("linuxX64") + val ios = fragments.create("iosX64") + native.refines(common) + linux.refines(native) + ios.refines(native) + } + project.evaluate() + + testDeserializeCompatibility(buildModel(project)) + } + + private fun buildModel(project: Project): IdeaKotlinProjectModel { + return project.serviceOf().getBuilder(IdeaKotlinProjectModel::class.java.name) + .buildAll(IdeaKotlinProjectModel::class.java.name, project) as IdeaKotlinProjectModel + } + + private fun testDeserializeCompatibility(model: IdeaKotlinProjectModel) { + val serializedModel = ByteArrayOutputStream().run { + ObjectOutputStream(this).use { stream -> stream.writeObject(model) } + toByteArray() + } + + val backwardsCompatibilityTestClassLoader = getClassLoaderForBackwardsCompatibilityTest() + val backwardsCompatibilityObjectInputStream = object : ObjectInputStream(ByteArrayInputStream(serializedModel)) { + override fun resolveClass(desc: ObjectStreamClass): Class<*> { + return backwardsCompatibilityTestClassLoader.loadClass(desc.name) + } + } + val backwardsCompatibilityModel = backwardsCompatibilityObjectInputStream.readObject() + assertSame( + backwardsCompatibilityModel.javaClass.classLoader, + backwardsCompatibilityTestClassLoader, + "Expected deserialized model being loaded by 'backwardsCompatibilityTestClassLoader'" + ) + + assertNotEquals>( + model.javaClass, backwardsCompatibilityModel.javaClass, + "Expected deserialized model java class to be different from origin" + ) + + assertEquals( + model.javaClass.name, backwardsCompatibilityModel.javaClass.name, + "Expected deserialized model to be same java class as origin" + ) + } +} + +private fun getClassLoaderForBackwardsCompatibilityTest(): ClassLoader { + val uris = getClasspathForBackwardsCompatibilityTest().map { file -> file.toURI().toURL() }.toTypedArray() + return URLClassLoader.newInstance(uris, null) +} + +private fun getClasspathForBackwardsCompatibilityTest(): List { + val backwardsCompatibilityClasspathString = System.getProperty("backwardsCompatibilityClasspath") + ?: error("Missing backwardsCompatibilityClasspath system property") + + return backwardsCompatibilityClasspathString.split(";").map { path -> File(path) } + .onEach { file -> if (!file.exists()) println("[WARNING] Missing $file") } +} +