[PL][tests] W/a for broken @Deprecated annotations in existing c-interop KLIBs
^KT-59030
This commit is contained in:
committed by
Space Team
parent
280caf003f
commit
502cbe8b17
@@ -46,6 +46,7 @@ versions.kotlinx-coroutines-core-jvm=1.5.0
|
||||
versions.kotlinx-coroutines-core=1.5.0
|
||||
|
||||
versions.kotlinx-metadata-jvm=0.6.0
|
||||
versions.kotlinx-metadata-klib=0.0.1-dev-10
|
||||
versions.kotlinx-serialization-json=1.3.3
|
||||
versions.ktor-network=1.4.0
|
||||
versions.ktor-utils=1.4.0
|
||||
|
||||
@@ -21,6 +21,7 @@ dependencies {
|
||||
testImplementation(project(":native:kotlin-native-utils"))
|
||||
testImplementation(project(":native:executors"))
|
||||
testApiJUnit5()
|
||||
testImplementation(commonDependency("org.jetbrains.kotlinx", "kotlinx-metadata-klib"))
|
||||
testImplementation(commonDependency("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false }
|
||||
|
||||
testRuntimeOnly(commonDependency("org.jetbrains.intellij.deps:trove4j"))
|
||||
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2010-2023 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.konan.blackboxtest
|
||||
|
||||
import kotlinx.metadata.KmAnnotation
|
||||
import kotlinx.metadata.KmAnnotationArgument
|
||||
import kotlinx.metadata.KmClass
|
||||
import kotlinx.metadata.KmDeclarationContainer
|
||||
import kotlinx.metadata.klib.KlibModuleMetadata
|
||||
import kotlinx.metadata.klib.annotations
|
||||
import org.jetbrains.kotlin.backend.common.serialization.*
|
||||
import org.jetbrains.kotlin.backend.common.serialization.proto.*
|
||||
import org.jetbrains.kotlin.backend.common.serialization.proto.IrDeclaration.DeclaratorCase.*
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.EnforcedHostTarget
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCase
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestCompilerArgs
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.TestKind
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationResult.Companion.assertSuccess
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UsePartialLinkage
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheMode
|
||||
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.CacheMode.WithStaticCache
|
||||
import org.jetbrains.kotlin.konan.file.unzipTo
|
||||
import org.jetbrains.kotlin.konan.file.zipDirAs
|
||||
import org.jetbrains.kotlin.library.*
|
||||
import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutForWriter
|
||||
import org.jetbrains.kotlin.library.impl.MetadataWriterImpl
|
||||
import org.junit.jupiter.api.Assumptions.assumeTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.Tag
|
||||
import org.junit.jupiter.api.Test
|
||||
import java.io.File
|
||||
import org.jetbrains.kotlin.konan.file.File as KFile
|
||||
|
||||
// See KT-59030.
|
||||
@Tag("partial-linkage")
|
||||
@EnforcedHostTarget
|
||||
@UsePartialLinkage(UsePartialLinkage.Mode.ENABLED_WITH_ERROR)
|
||||
class KT59030WorkaroundTest : AbstractNativeSimpleTest() {
|
||||
// This test relies on static caches. So, run it along with other PL tests but only when caches are enabled.
|
||||
@BeforeEach
|
||||
fun assumeOnlyStaticCacheEverywhere() {
|
||||
val cacheMode = testRunSettings.get<CacheMode>()
|
||||
assumeTrue(cacheMode is WithStaticCache)
|
||||
assumeTrue(cacheMode.useStaticCacheForUserLibraries)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun kt59030() {
|
||||
val library = cinteropToLibrary(
|
||||
targets = targets,
|
||||
defFile = File(DEF_FILE_PATH),
|
||||
outputDir = buildDir,
|
||||
freeCompilerArgs = TestCompilerArgs.EMPTY
|
||||
).assertSuccess().resultingArtifact
|
||||
spoilDeprecatedAnnotationsInLibrary(library)
|
||||
|
||||
compileToExecutable(
|
||||
generateTestCaseWithSingleFile(
|
||||
sourceFile = File(MAIN_FILE_PATH),
|
||||
testKind = TestKind.STANDALONE_NO_TR,
|
||||
extras = TestCase.NoTestRunnerExtras("main")
|
||||
),
|
||||
library.asLibraryDependency()
|
||||
).assertSuccess()
|
||||
}
|
||||
|
||||
private fun spoilDeprecatedAnnotationsInLibrary(klib: TestCompilationArtifact.KLIB) {
|
||||
// Make a backup.
|
||||
val oldLibraryFile = KFile(with(klib.klibFile) { parentFile.newDir("__backup__").resolve(name).path })
|
||||
val newLibraryFile = KFile(klib.klibFile.path)
|
||||
newLibraryFile.renameTo(oldLibraryFile)
|
||||
|
||||
// Unzip the new library.
|
||||
val newLibraryTmpDir = KFile(newLibraryFile.path + ".tmp")
|
||||
oldLibraryFile.unzipTo(newLibraryTmpDir)
|
||||
|
||||
// Read the library.
|
||||
val oldLibrary = resolveSingleFileKlib(oldLibraryFile, strategy = ToolingSingleFileKlibResolveStrategy)
|
||||
val newLibraryLayout = KotlinLibraryLayoutForWriter(newLibraryFile, newLibraryTmpDir)
|
||||
|
||||
// Patch the library.
|
||||
spoilDeprecatedAnnotationsInMetadata(oldLibrary, newLibraryLayout)
|
||||
|
||||
// Zip and clean-up.
|
||||
newLibraryTmpDir.zipDirAs(newLibraryFile)
|
||||
newLibraryTmpDir.deleteRecursively()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TEST_DATA_DIR = "kotlin-native/backend.native/tests/interop/basics"
|
||||
const val DEF_FILE_PATH = "${TEST_DATA_DIR}/cvectors.def"
|
||||
const val MAIN_FILE_PATH = "${TEST_DATA_DIR}/vectors.kt"
|
||||
|
||||
private const val DEPRECATED_CLASS_NAME = "kotlin/Deprecated"
|
||||
private const val REPLACE_WITH_ARG = "replaceWith"
|
||||
private const val EXPRESSION_ARG = "expression"
|
||||
|
||||
private fun File.newDir(name: String): File = resolve(name).apply { mkdirs() }
|
||||
|
||||
private fun spoilDeprecatedAnnotationsInMetadata(
|
||||
oldLibrary: KotlinLibrary,
|
||||
newLibraryLayout: KotlinLibraryLayoutForWriter,
|
||||
) {
|
||||
// Read the metadata.
|
||||
val moduleMetadata = KlibModuleMetadata.read(
|
||||
object : KlibModuleMetadata.MetadataLibraryProvider {
|
||||
override val moduleHeaderData get() = oldLibrary.moduleHeaderData
|
||||
override fun packageMetadataParts(fqName: String) = oldLibrary.packageMetadataParts(fqName)
|
||||
override fun packageMetadata(fqName: String, partName: String) = oldLibrary.packageMetadata(fqName, partName)
|
||||
}
|
||||
)
|
||||
|
||||
// Patch the metadata.
|
||||
moduleMetadata.fragments.forEach { fragment ->
|
||||
fragment.pkg?.let(this::spoilDeprecatedAnnotationsInMetadataContainer)
|
||||
fragment.classes.forEach(this::spoilDeprecatedAnnotationsInMetadataClass)
|
||||
}
|
||||
|
||||
// Write back the metadata.
|
||||
val serializedMetadata = with(moduleMetadata.write()) {
|
||||
SerializedMetadata(module = header, fragments, fragmentNames)
|
||||
}
|
||||
|
||||
newLibraryLayout.metadataDir.deleteRecursively() // Drop old metadata.
|
||||
MetadataWriterImpl(newLibraryLayout).addMetadata(serializedMetadata) // Write new metadata.
|
||||
}
|
||||
|
||||
private fun spoilDeprecatedAnnotationsInMetadataContainer(container: KmDeclarationContainer) {
|
||||
container.functions.forEach { spoilDeprecatedAnnotationsInMetadataAnnotationList(it.annotations) }
|
||||
container.properties.forEach { spoilDeprecatedAnnotationsInMetadataAnnotationList(it.annotations) }
|
||||
container.typeAliases.forEach { spoilDeprecatedAnnotationsInMetadataAnnotationList(it.annotations) }
|
||||
}
|
||||
|
||||
private fun spoilDeprecatedAnnotationsInMetadataClass(clazz: KmClass) {
|
||||
spoilDeprecatedAnnotationsInMetadataAnnotationList(clazz.annotations)
|
||||
clazz.constructors.forEach { spoilDeprecatedAnnotationsInMetadataAnnotationList(it.annotations) }
|
||||
spoilDeprecatedAnnotationsInMetadataContainer(clazz)
|
||||
}
|
||||
|
||||
private fun spoilDeprecatedAnnotationsInMetadataAnnotationList(annotations: MutableList<KmAnnotation>) {
|
||||
annotations.replaceAll { annotation ->
|
||||
if (annotation.className == DEPRECATED_CLASS_NAME) spoilDeprecatedAnnotationInMetadata(annotation) else annotation
|
||||
}
|
||||
}
|
||||
|
||||
private fun spoilDeprecatedAnnotationInMetadata(deprecated: KmAnnotation): KmAnnotation =
|
||||
deprecated.copy(
|
||||
arguments = deprecated.arguments.mapValues { (argName, argValue) ->
|
||||
if (argName == REPLACE_WITH_ARG) spoilReplaceWithAnnotationInMetadata(argValue.unwrap()).wrap() else argValue
|
||||
}
|
||||
)
|
||||
|
||||
private fun spoilReplaceWithAnnotationInMetadata(replaceWith: KmAnnotation): KmAnnotation =
|
||||
replaceWith.copy(
|
||||
arguments = replaceWith.arguments.filterKeys { argName -> argName != EXPRESSION_ARG }
|
||||
)
|
||||
|
||||
private fun KmAnnotationArgument<*>.unwrap(): KmAnnotation = (this as KmAnnotationArgument.AnnotationValue).value
|
||||
private fun KmAnnotation.wrap(): KmAnnotationArgument.AnnotationValue = KmAnnotationArgument.AnnotationValue(this)
|
||||
}
|
||||
}
|
||||
+63
-4
@@ -20,8 +20,6 @@ import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||
import org.junit.jupiter.api.Assumptions
|
||||
import java.io.File
|
||||
|
||||
private val DEFAULT_EXTRAS = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
|
||||
|
||||
internal abstract class ArtifactBuilder<T>(
|
||||
val test: AbstractNativeSimpleTest,
|
||||
val rootDir: File,
|
||||
@@ -100,6 +98,7 @@ internal class ExecutableBuilder(
|
||||
}
|
||||
|
||||
internal val AbstractNativeSimpleTest.buildDir: File get() = testRunSettings.get<Binaries>().testBinariesDir
|
||||
internal val AbstractNativeSimpleTest.targets: KotlinNativeTargets get() = testRunSettings.get()
|
||||
|
||||
internal fun TestCompilationArtifact.KLIB.asLibraryDependency() =
|
||||
ExistingDependency(this, TestCompilationDependencyType.Library)
|
||||
@@ -130,6 +129,22 @@ internal fun AbstractNativeSimpleTest.compileToLibrary(
|
||||
return compilationResult.resultingArtifact
|
||||
}
|
||||
|
||||
internal fun AbstractNativeSimpleTest.cinteropToLibrary(
|
||||
targets: KotlinNativeTargets,
|
||||
defFile: File,
|
||||
outputDir: File,
|
||||
freeCompilerArgs: TestCompilerArgs
|
||||
): TestCompilationResult<out TestCompilationArtifact.KLIB> {
|
||||
val testCase: TestCase = generateCInteropTestCaseFromSingleDefFile(defFile, freeCompilerArgs)
|
||||
return CInteropCompilation(
|
||||
classLoader = testRunSettings.get(),
|
||||
targets = targets,
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
defFile = testCase.modules.single().files.single().location,
|
||||
expectedArtifact = getLibraryArtifact(testCase, outputDir)
|
||||
).result
|
||||
}
|
||||
|
||||
internal class CompiledExecutable(
|
||||
val testCase: TestCase,
|
||||
val compilationResult: TestCompilationResult.Success<out TestCompilationArtifact.Executable>
|
||||
@@ -191,7 +206,51 @@ internal fun AbstractNativeSimpleTest.generateTestCaseWithSingleModule(
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
nominalPackageName = PackageName.EMPTY,
|
||||
checks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
|
||||
extras = DEFAULT_EXTRAS
|
||||
extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
|
||||
).apply {
|
||||
initialize(null, null)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun AbstractNativeSimpleTest.generateTestCaseWithSingleFile(
|
||||
sourceFile: File,
|
||||
freeCompilerArgs: TestCompilerArgs = TestCompilerArgs.EMPTY,
|
||||
testKind: TestKind = TestKind.STANDALONE,
|
||||
extras: TestCase.Extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
|
||||
): TestCase {
|
||||
val moduleName: String = sourceFile.name ?: LAUNCHER_MODULE_NAME
|
||||
val module = TestModule.Exclusive(moduleName, emptySet(), emptySet(), emptySet())
|
||||
module.files += TestFile.createCommitted(sourceFile, module)
|
||||
|
||||
return TestCase(
|
||||
id = TestCaseId.Named(moduleName),
|
||||
kind = testKind,
|
||||
modules = setOf(module),
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
nominalPackageName = PackageName.EMPTY,
|
||||
checks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
|
||||
extras = extras
|
||||
).apply {
|
||||
initialize(null, null)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun AbstractNativeSimpleTest.generateCInteropTestCaseFromSingleDefFile(
|
||||
defFile: File,
|
||||
freeCompilerArgs: TestCompilerArgs,
|
||||
): TestCase {
|
||||
val moduleName: String = defFile.name
|
||||
val module = TestModule.Exclusive(moduleName, emptySet(), emptySet(), emptySet())
|
||||
module.files += TestFile.createCommitted(defFile, module)
|
||||
|
||||
return TestCase(
|
||||
id = TestCaseId.Named(moduleName),
|
||||
kind = TestKind.STANDALONE,
|
||||
modules = setOf(module),
|
||||
freeCompilerArgs = freeCompilerArgs,
|
||||
nominalPackageName = PackageName.EMPTY,
|
||||
checks = TestRunChecks.Default(testRunSettings.get<Timeouts>().executionTimeout),
|
||||
extras = TestCase.WithTestRunnerExtras(TestRunnerType.DEFAULT)
|
||||
).apply {
|
||||
initialize(null, null)
|
||||
}
|
||||
@@ -220,7 +279,7 @@ private fun AbstractNativeSimpleTest.compileToExecutable(
|
||||
settings = testRunSettings,
|
||||
freeCompilerArgs = testCase.freeCompilerArgs,
|
||||
sourceModules = testCase.modules,
|
||||
extras = DEFAULT_EXTRAS,
|
||||
extras = testCase.extras,
|
||||
dependencies = dependencies,
|
||||
expectedArtifact = getExecutableArtifact()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user