diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmClassLoaderProjectSerializer.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmClassLoaderProjectSerializer.kt new file mode 100644 index 00000000000..fa13d59150f --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmClassLoaderProjectSerializer.kt @@ -0,0 +1,95 @@ +/* + * 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.kpm.idea.testFixtures + +import org.jetbrains.kotlin.gradle.kpm.idea.IdeaKpmProject +import org.jetbrains.kotlin.kpm.idea.proto.IdeaKpmProject +import org.jetbrains.kotlin.kpm.idea.proto.toByteArray +import org.jetbrains.kotlin.tooling.core.UnsafeApi +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method +import java.lang.reflect.Proxy +import kotlin.reflect.full.primaryConstructor +import kotlin.reflect.jvm.javaGetter +import kotlin.reflect.jvm.javaMethod + +@OptIn(UnsafeApi::class) +fun TestIdeaKpmClassLoaderProjectSerializer(): TestIdeaKpmClassLoaderProjectSerializer = + TestIdeaKpmProtoClassLoaderProjectSerializer(TestIdeaKpmClassLoaderProjectSerializer::class.java.classLoader) + + +@OptIn(UnsafeApi::class) +fun TestIdeaKpmClassLoaderProjectSerializer(classLoader: ClassLoader): TestIdeaKpmClassLoaderProjectSerializer { + /* + Instantiates the `TestIdeaKpmProtoClassLoaderProjectSerializer` in the previous version of the classes + (using the specified classLoader). A java proxy will be used to bridge this implementation and the return type interface + */ + val serializerInstance = classLoader.loadClass(TestIdeaKpmProtoClassLoaderProjectSerializer::class.java.name) + .kotlin.primaryConstructor?.call(classLoader) ?: error( + "Failed to construct ${TestIdeaKpmProtoClassLoaderProjectSerializer::class.java.name} in $classLoader" + ) + + return Proxy.newProxyInstance( + /* loader = */ TestIdeaKpmClassLoaderProjectSerializer::class.java.classLoader, + /* interfaces = */ arrayOf(TestIdeaKpmClassLoaderProjectSerializer::class.java), + /* h = */ ProxyInvocationHandler(classLoader, serializerInstance) + ) as TestIdeaKpmClassLoaderProjectSerializer +} + +/** + * Test Util to serialize / deserialize [IdeaKpmProject] within a dedicated ClassLoader. + * The serialization context used will be [TestIdeaKpmSerializationContext]. Note, that this context + * might also depend on the version shipped by the specified [ClassLoader]. + */ +interface TestIdeaKpmClassLoaderProjectSerializer { + val classLoader: ClassLoader + val reports: List + fun serialize(project: Any): ByteArray + fun deserialize(data: ByteArray): Any? +} + +@UnsafeApi +internal class TestIdeaKpmProtoClassLoaderProjectSerializer( + override val classLoader: ClassLoader +) : TestIdeaKpmClassLoaderProjectSerializer { + private val context = TestIdeaKpmSerializationContext() + + override val reports: List + get() = context.logger.reports + + override fun serialize(project: Any): ByteArray { + return (project as IdeaKpmProject).toByteArray(context) + } + + override fun deserialize(data: ByteArray): Any? { + return context.IdeaKpmProject(data) + } +} + +private class ProxyInvocationHandler( + private val classLoader: ClassLoader, + private val serializerInstance: Any +) : InvocationHandler { + override fun invoke(proxy: Any, method: Method, args: Array?): Any? { + if (method == TestIdeaKpmClassLoaderProjectSerializer::classLoader.getter.javaMethod) { + return classLoader + } + + val targetMethod = serializerInstance.javaClass.methods.find { it.name == method.name } ?: error("Missing $method") + val result = targetMethod.invoke(serializerInstance, *args.orEmpty()) + + /* + The result objects here are also part of the test-fixtures, which will have different classes, depending on the + ClassLoader being used. The reports here, will be copied (serialized and then deserialized in this ClassLoader). + */ + if (method == TestIdeaKpmClassLoaderProjectSerializer::reports.javaGetter) { + /* Copy into 'our' ClassLoader */ + return result?.copy() + } + + return result + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmExtra.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmExtra.kt new file mode 100644 index 00000000000..ff7d4b17c37 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmExtra.kt @@ -0,0 +1,10 @@ +/* + * 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.kpm.idea.testFixtures + +import java.io.Serializable + +data class TestIdeaKpmExtra(val id: Any) : Serializable diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmExtrasSerializationExtension.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmExtrasSerializationExtension.kt new file mode 100644 index 00000000000..b5881535e95 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmExtrasSerializationExtension.kt @@ -0,0 +1,27 @@ +/* + * 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.kpm.idea.testFixtures + +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializationExtension +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializer +import org.jetbrains.kotlin.tooling.core.Extras +import org.jetbrains.kotlin.tooling.core.Type +import org.jetbrains.kotlin.tooling.core.extrasKeyOf + +@Suppress("UNCHECKED_CAST") +object TestIdeaKpmExtrasSerializationExtension : IdeaKpmExtrasSerializationExtension { + + val ignoredStringKey = extrasKeyOf("ignored") + val anySerializableKey = extrasKeyOf("serializable") + + override fun serializer(key: Extras.Key): IdeaKpmExtrasSerializer? = when { + key == ignoredStringKey -> null + key == anySerializableKey -> IdeaKpmExtrasSerializer.javaIoSerializable() + key.type == Type() -> TestIdeaKpmStringExtrasSerializer + key.type == Type() -> TestIdeaKpmIntExtrasSerializer + else -> null + } as? IdeaKpmExtrasSerializer +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmIntExtrasSerializer.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmIntExtrasSerializer.kt new file mode 100644 index 00000000000..c55b4a07d7b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmIntExtrasSerializer.kt @@ -0,0 +1,20 @@ +/* + * 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.kpm.idea.testFixtures + +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializer +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationContext +import java.nio.ByteBuffer + +object TestIdeaKpmIntExtrasSerializer : IdeaKpmExtrasSerializer { + override fun serialize(context: IdeaKpmSerializationContext, value: Int): ByteArray { + return ByteBuffer.allocate(Int.SIZE_BYTES).putInt(value).array() + } + + override fun deserialize(context: IdeaKpmSerializationContext, data: ByteArray): Int { + return ByteBuffer.wrap(data).int + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmSerializationContext.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmSerializationContext.kt new file mode 100644 index 00000000000..98fa88cd79b --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmSerializationContext.kt @@ -0,0 +1,13 @@ +/* + * 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.kpm.idea.testFixtures + +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationContext + +class TestIdeaKpmSerializationContext : IdeaKpmSerializationContext { + override val logger = TestIdeaKpmSerializationLogger() + override val extrasSerializationExtension = TestIdeaKpmExtrasSerializationExtension +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmSerializationLogger.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmSerializationLogger.kt new file mode 100644 index 00000000000..407c8ecbb65 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmSerializationLogger.kt @@ -0,0 +1,29 @@ +/* + * 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.kpm.idea.testFixtures + +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationLogger +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationLogger.Severity +import java.io.Serializable +import kotlin.collections.toList + +class TestIdeaKpmSerializationLogger : IdeaKpmSerializationLogger { + data class Report( + val severity: Severity, + val message: String, + val cause: Throwable? = null + ) : Serializable { + override fun toString(): String = "[${severity.name}]: $message" + } + + private val _reports = mutableListOf() + + val reports get() = _reports.toList() + + override fun report(severity: Severity, message: String, cause: Throwable?) { + _reports.add(Report(severity, message, cause)) + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmStringExtrasSerializer.kt b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmStringExtrasSerializer.kt new file mode 100644 index 00000000000..2cfd361d335 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-idea/src/testFixtures/kotlin/org/jetbrains/kotlin/gradle/kpm/idea/testFixtures/TestIdeaKpmStringExtrasSerializer.kt @@ -0,0 +1,16 @@ +/* + * 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.kpm.idea.testFixtures + +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmExtrasSerializer +import org.jetbrains.kotlin.gradle.kpm.idea.serialize.IdeaKpmSerializationContext +import kotlin.text.decodeToString +import kotlin.text.encodeToByteArray + +object TestIdeaKpmStringExtrasSerializer : IdeaKpmExtrasSerializer { + override fun serialize(context: IdeaKpmSerializationContext, value: String): ByteArray = value.encodeToByteArray() + override fun deserialize(context: IdeaKpmSerializationContext, data: ByteArray) = data.decodeToString() +}