[KLIB] ABI reader: Refactor manifest reading test ^KT-54402
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestReadingTest.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestReadingTest.kt new file mode 100644 index 0000000..3d7a54a --- /dev/null +++ b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestReadingTest.kt
@@ -0,0 +1,102 @@ +/* + * 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.library.abi + +import com.intellij.openapi.util.io.FileUtil.createTempDirectory +import org.jetbrains.kotlin.library.* +import org.jetbrains.kotlin.library.impl.BuiltInsPlatform +import org.jetbrains.kotlin.library.impl.KotlinLibraryLayoutForWriter +import org.jetbrains.kotlin.library.impl.KotlinLibraryWriterImpl +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInfo +import java.io.File +import java.util.* +import org.jetbrains.kotlin.konan.file.File as KFile + +@OptIn(ExperimentalLibraryAbiReader::class) +class ManifestReadingTest { + private lateinit var buildDir: File + + @BeforeEach + fun setUp(testInfo: TestInfo) { + buildDir = createTempDirectory(testInfo.testClass.get().simpleName, testInfo.testMethod.get().name) + } + + @AfterEach + fun tearDown() { + buildDir.deleteRecursively() + } + + @Test + fun testManifestReading() { + val testData = mapOf( + "sample-library-1" to LibraryManifest( + platform = BuiltInsPlatform.JS.name, + nativeTargets = emptyList(), + compilerVersion = "1.23.45", + abiVersion = "2.34.56", + libraryVersion = "3.45.67", + irProviderName = "test_ir_provider_123" + ), + "sample-library-2" to LibraryManifest( + platform = BuiltInsPlatform.NATIVE.name, + nativeTargets = listOf("ios_arm64", "ios_simulator_arm64", "macos_arm64", "macos_x64"), + compilerVersion = null, + abiVersion = null, + libraryVersion = null, + irProviderName = null + ), + ) + + testData.forEach { (libraryName, originalManifest) -> + val libraryFile = createEmptyLibraryWithSpecificManifest(libraryName, originalManifest) + val readManifest = LibraryAbiReader.readAbiInfo(libraryFile).manifest + + assertTrue(originalManifest !== readManifest) { "Library name: $libraryName" } + assertEquals(originalManifest, readManifest) { "Library name: $libraryName" } + } + } + + private fun createEmptyLibraryWithSpecificManifest(libraryName: String, libraryManifest: LibraryManifest): File { + val libraryVersioning = KotlinLibraryVersioning( + libraryVersion = libraryManifest.libraryVersion, + compilerVersion = libraryManifest.compilerVersion, + abiVersion = libraryManifest.abiVersion?.parseKotlinAbiVersion(), + metadataVersion = null + ) + val builtInsPlatform = libraryManifest.platform?.let(BuiltInsPlatform::parseFromString) + ?: error("Unknown platform: ${libraryManifest.platform}") + + val libraryFile = buildDir.resolve("$libraryName.klib") + val libraryKFile = KFile(libraryFile.absolutePath) + + val libraryLayout = KotlinLibraryLayoutForWriter(libraryKFile, libraryKFile) + val library = KotlinLibraryWriterImpl( + moduleName = libraryName, + versions = libraryVersioning, + builtInsPlatform = builtInsPlatform, + nativeTargets = libraryManifest.nativeTargets, + nopack = true, + shortName = libraryName, + layout = libraryLayout + ) + libraryManifest.irProviderName?.let { irProviderName -> + library.addManifestAddend( + Properties().apply { + this[KLIB_PROPERTY_IR_PROVIDER] = irProviderName + } + ) + } + library.addIr(SerializedIrModule(files = emptyList())) // Empty library. + library.commit() + + return libraryFile + } +}
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestRenderingTest.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestRenderingTest.kt deleted file mode 100644 index 9c69c5f..0000000 --- a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/ManifestRenderingTest.kt +++ /dev/null
@@ -1,45 +0,0 @@ -/* - * 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.library.abi - -import org.jetbrains.kotlin.library.impl.BuiltInsPlatform -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.TestInfo -import java.io.File - -@OptIn(ExperimentalLibraryAbiReader::class) -class ManifestRenderingTest { - private lateinit var buildDir: File - private lateinit var sourceFile: File - - @BeforeEach - fun setUp(testInfo: TestInfo) { - buildDir = setUpBuildDir(testInfo) - sourceFile = buildDir.resolve("source.kt").apply { createNewFile() } // Just an empty file. - } - - @Test - fun renderWithManifest() { - val customManifest = LibraryManifest( - platform = BuiltInsPlatform.JS.name, - nativeTargets = listOf("ios_arm64", "ios_simulator_arm64", "macos_arm64", "macos_x64"), - compilerVersion = "1.23.45", - abiVersion = "2.34.56", - libraryVersion = "3.45.67", - irProviderName = "test_ir_provider_123" - ) - - val libraryFile = buildLibrary(sourceFile, libraryName = "sample-library", buildDir) - patchManifest(libraryFile, customManifest) - - val readManifest = LibraryAbiReader.readAbiInfo(libraryFile).manifest - assertTrue(customManifest !== readManifest) - assertEquals(customManifest, readManifest) - } -}
diff --git a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/utils.kt b/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/utils.kt deleted file mode 100644 index ee73ede..0000000 --- a/compiler/util-klib-abi/test/org/jetbrains/kotlin/library/abi/utils.kt +++ /dev/null
@@ -1,160 +0,0 @@ -/* - * 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.library.abi - -import com.intellij.openapi.vfs.StandardFileSystems -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.openapi.vfs.local.CoreLocalFileSystem -import com.intellij.psi.PsiManager -import com.intellij.psi.SingleRootFileViewProvider -import org.jetbrains.kotlin.cli.common.messages.MessageRenderer -import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector -import org.jetbrains.kotlin.cli.js.klib.compileModuleToAnalyzedFirWithPsi -import org.jetbrains.kotlin.cli.js.klib.serializeFirKlib -import org.jetbrains.kotlin.cli.js.klib.transformFirToIr -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.Companion.createForParallelTests -import org.jetbrains.kotlin.config.CommonConfigurationKeys -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.diagnostics.DiagnosticReporterFactory -import org.jetbrains.kotlin.ir.backend.js.MainModule -import org.jetbrains.kotlin.ir.backend.js.ModulesStructure -import org.jetbrains.kotlin.js.config.JSConfigurationKeys -import org.jetbrains.kotlin.konan.file.ZipFileSystemCacheableAccessor -import org.jetbrains.kotlin.konan.file.unzipTo -import org.jetbrains.kotlin.konan.file.zipDirAs -import org.jetbrains.kotlin.konan.properties.saveToFile -import org.jetbrains.kotlin.library.* -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.serialization.js.ModuleKind -import org.jetbrains.kotlin.test.utils.TestDisposable -import org.junit.jupiter.api.Assertions.* -import org.junit.jupiter.api.TestInfo -import java.io.ByteArrayOutputStream -import java.io.File -import java.io.PrintStream -import java.nio.charset.Charset -import org.jetbrains.kotlin.konan.file.File as KFile - -private val TestInfo.className: String get() = testClass.orElseGet { fail("Can't get test class name") }.simpleName -private val TestInfo.methodName: String get() = testMethod.orElseGet { fail("Can't get test method name") }.name - -internal fun setUpBuildDir(testInfo: TestInfo): File { - val projectBuildDir = System.getenv(ENV_VAR_PROJECT_BUILD_DIR) - ?.let { File(it) } - ?: fail<Nothing>("$ENV_VAR_PROJECT_BUILD_DIR environment variable not specified") - - assertTrue(projectBuildDir.isDirectory) { "Project build dir does not exist: $projectBuildDir" } - - return projectBuildDir.resolve("t").resolve("${testInfo.className}.${testInfo.methodName}").apply { - deleteRecursively() - mkdirs() - } -} - -internal fun buildLibrary(sourceFile: File, libraryName: String, buildDir: File): File { - val configuration = CompilerConfiguration() - val environment = createForParallelTests(TestDisposable(), configuration, EnvironmentConfigFiles.JS_CONFIG_FILES) - - configuration.put(CommonConfigurationKeys.MODULE_NAME, libraryName) - configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN) - configuration.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true) - configuration.put(JSConfigurationKeys.ZIP_FILE_SYSTEM_ACCESSOR, ZipFileSystemCacheableAccessor(2)) - - val psiManager = PsiManager.getInstance(environment.project) - val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL) as CoreLocalFileSystem - - val virtualFile = fileSystem.findFileByIoFile(sourceFile) ?: error("VirtualFile for $sourceFile not found") - val ktFiles = SingleRootFileViewProvider(psiManager, virtualFile).allFiles.filterIsInstance<KtFile>() - - val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter() - - val jsStdlib = File("libraries/stdlib/js-ir/build/classes/kotlin/js/main").absoluteFile - val dependencies = listOf(jsStdlib.absolutePath) - - val moduleStructure = ModulesStructure( - project = environment.project, - mainModule = MainModule.SourceFiles(ktFiles), - compilerConfiguration = configuration, - dependencies = dependencies, - friendDependenciesPaths = emptyList() - ) - - val outputStream = ByteArrayOutputStream() - val messageCollector = PrintingMessageCollector(PrintStream(outputStream), MessageRenderer.PLAIN_FULL_PATHS, true) - - val analyzedOutput = compileModuleToAnalyzedFirWithPsi( - moduleStructure = moduleStructure, - ktFiles = ktFiles, - libraries = dependencies, - friendLibraries = emptyList(), - diagnosticsReporter = diagnosticsReporter, - incrementalDataProvider = null, - lookupTracker = null - ) - - val fir2IrActualizedResult = transformFirToIr(moduleStructure, analyzedOutput.output, diagnosticsReporter) - - if (analyzedOutput.reportCompilationErrors(moduleStructure, diagnosticsReporter, messageCollector)) { - val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8")) - fail<Nothing>("The following errors occurred compiling test:\n$messages") - } - - val libraryDir = KFile(buildDir.resolve(libraryName).absolutePath) - val libraryFile = KFile(libraryDir.absolutePath + ".klib") - - serializeFirKlib( - moduleStructure = moduleStructure, - firOutputs = analyzedOutput.output, - fir2IrActualizedResult = fir2IrActualizedResult, - outputKlibPath = libraryDir.absolutePath, - messageCollector = messageCollector, - diagnosticsReporter = diagnosticsReporter, - jsOutputName = libraryName - ) - - if (messageCollector.hasErrors()) { - val messages = outputStream.toByteArray().toString(Charset.forName("UTF-8")) - fail<Nothing>("The following errors occurred serializing test klib:\n$messages") - } - - libraryDir.zipDirAs(libraryFile) - libraryDir.deleteRecursively() - - return File(libraryFile.absolutePath) -} - -@OptIn(ExperimentalLibraryAbiReader::class) -internal fun patchManifest(libraryFile: File, customManifest: LibraryManifest) { - val libraryKFile = KFile(libraryFile.absolutePath) - val manifestProperties = resolveSingleFileKlib(libraryKFile).manifestProperties - - fun set(name: String, value: String?) { - assertTrue(!value.isNullOrBlank()) { "$name has invalid value: [$value]" } - manifestProperties[name] = value!! - } - - set(KLIB_PROPERTY_BUILTINS_PLATFORM, customManifest.platform) - set(KLIB_PROPERTY_NATIVE_TARGETS, customManifest.nativeTargets.joinToString(" ")) - set(KLIB_PROPERTY_COMPILER_VERSION, customManifest.compilerVersion) - set(KLIB_PROPERTY_ABI_VERSION, customManifest.abiVersion) - set(KLIB_PROPERTY_LIBRARY_VERSION, customManifest.libraryVersion) - set(KLIB_PROPERTY_IR_PROVIDER, customManifest.irProviderName) - - assertEquals(libraryFile.extension, "klib") - val libraryDir = libraryFile.resolveSibling(libraryFile.nameWithoutExtension) - val libraryKDir = KFile(libraryDir.absolutePath) - libraryKFile.unzipTo(libraryKDir) - libraryKFile.deleteRecursively() - - libraryDir.walkTopDown() - .filter { it.isFile && it.name == "manifest" } - .forEach { manifestProperties.saveToFile(KFile(it.absolutePath)) } - - libraryKDir.zipDirAs(libraryKFile) -} - -private const val ENV_VAR_PROJECT_BUILD_DIR = "PROJECT_BUILD_DIR"