[KLIB] Add klib layout tests

- currently only file paths are checked
This commit is contained in:
Roman Artemev
2021-12-08 15:56:07 +03:00
committed by teamcity
parent fb84287ae0
commit 1a64e2da91
2 changed files with 239 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
// FILE: a.kt
fun foo() = 42
// FILE: b/c.kt
fun bar() = foo()
class C
// FILE: c/d/e.kt
fun qux(c: C) = c.toString()
fun baz() = qux(C()) + bar()
@@ -0,0 +1,224 @@
/*
* Copyright 2010-2021 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.klib
import com.intellij.openapi.vfs.StandardFileSystems
import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiManager
import org.jetbrains.kotlin.backend.common.serialization.codedInputStream
import org.jetbrains.kotlin.backend.common.serialization.proto.IrFile
import org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.codegen.CodegenTestCase
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.incremental.md5
import org.jetbrains.kotlin.ir.backend.js.ModulesStructure
import org.jetbrains.kotlin.ir.backend.js.generateKLib
import org.jetbrains.kotlin.ir.backend.js.jsResolveLibraries
import org.jetbrains.kotlin.ir.backend.js.prepareAnalyzedSourceModule
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.util.KtTestUtil
import org.jetbrains.kotlin.util.DummyLogger
import java.io.File
class FilePathsInKlibTest : CodegenTestCase() {
companion object {
private const val MODULE_NAME = "M"
private const val testDataFile = "compiler/testData/ir/klibLayout/multiFiles.kt"
}
private fun loadKtFiles(directory: File): List<KtFile> {
val psiManager = PsiManager.getInstance(myEnvironment.project)
val fileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL)
val vDirectory = fileSystem.findFileByPath(directory.canonicalPath) ?: error("File not found: $directory")
return psiManager.findDirectory(vDirectory)?.files?.map { it as KtFile } ?: error("Cannot load KtFiles")
}
private val runtimeKlibPath = "libraries/stdlib/js-ir/build/classes/kotlin/js/main"
private fun analyseKtFiles(configuration: CompilerConfiguration, ktFiles: List<KtFile>): ModulesStructure {
return prepareAnalyzedSourceModule(
myEnvironment.project,
ktFiles,
configuration,
listOf(runtimeKlibPath),
emptyList(),
AnalyzerWithCompilerReport(configuration),
)
}
private fun produceKlib(module: ModulesStructure, destination: File) {
generateKLib(module, irFactory = IrFactoryImpl, outputKlibPath = destination.path, nopack = false, jsOutputName = MODULE_NAME)
}
private fun setupEnvironment(): CompilerConfiguration {
val configuration = CompilerConfiguration()
myEnvironment = KotlinCoreEnvironment.createForTests(testRootDisposable, configuration, EnvironmentConfigFiles.JS_CONFIG_FILES)
configuration.put(CommonConfigurationKeys.MODULE_NAME, MODULE_NAME)
return configuration
}
private fun File.md5(): Long = readBytes().md5()
private fun File.loadKlibFilePaths(denormalize: Boolean): List<String> {
val libs = jsResolveLibraries(listOf(runtimeKlibPath, canonicalPath), emptyList(), DummyLogger).getFullList()
val lib = libs.last()
val fileSize = lib.fileCount()
val extReg = ExtensionRegistryLite.newInstance()
val result = ArrayList<String>(fileSize)
for (i in 0 until fileSize) {
val fileStream = lib.file(i).codedInputStream
val fileProto = IrFile.parseFrom(fileStream, extReg)
val fileName = fileProto.fileEntry.name
if (denormalize) {
result.add(fileName.replace("/", File.separator))
} else {
result.add(fileName)
}
}
return result
}
private fun createTestFiles(): List<TestFile> {
val file = File(testDataFile)
val expectedText = KtTestUtil.doLoadFile(file)
return createTestFilesFromFile(file, expectedText)
}
private fun compileKlibs(testFiles: List<TestFile>, configuration: CompilerConfiguration, workingDir: File): File {
for (testFile in testFiles) {
val testFileA = File(workingDir, testFile.name).also { it.parentFile.let { p -> if (!p.exists()) p.mkdirs() } }
testFileA.writeText(testFile.content)
}
val ktFilesA = loadKtFiles(workingDir)
val module = analyseKtFiles(configuration, ktFilesA)
val artifact = File(workingDir, "$MODULE_NAME.klib")
produceKlib(module, artifact)
return artifact
}
// @Ignore
// fun testAbsolutePaths() {
// // TODO: since the current behaviour is random (see PsiIrFileEntry::psiFileName and CoreLocalVirtualFile::getName/getPath)
// // this test currently disabled until normalization by default turn on
// val testFiles = createTestFiles()
// val workingPath = kotlin.io.path.createTempDirectory()
// val workingDirFile = workingPath.toFile().also { assert(it.isDirectory) }
//
// try {
// val artifact = compileKlibs(testFiles, setupEnvironment(), workingDirFile)
// val modulePaths = artifact.loadKlibFilePaths(denormalize = false)
// val dirCanonicalPaths = workingDirFile.listFiles { _, name -> name.endsWith(".kt") }!!.map { it.canonicalPath }
//
// assertSameElements(modulePaths, dirCanonicalPaths)
//
// } finally {
// workingDirFile.deleteRecursively()
// }
// }
fun testStableCompilation() {
val testFiles = createTestFiles()
val dirAPath = kotlin.io.path.createTempDirectory()
val dirBPath = kotlin.io.path.createTempDirectory()
val dirAFile = dirAPath.toFile().also { assert(it.isDirectory) }
val dirBFile = dirBPath.toFile().also { assert(it.isDirectory) }
try {
val configuration = setupEnvironment()
configuration.put(CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES, listOf(dirAFile.canonicalPath, dirBFile.canonicalPath))
val moduleA = compileKlibs(testFiles, configuration, dirAFile)
val moduleB = compileKlibs(testFiles, configuration, dirBFile)
assertEquals(moduleA.md5(), moduleB.md5())
} finally {
dirAFile.deleteRecursively()
dirBFile.deleteRecursively()
}
}
fun testRelativePaths() {
val testFiles = createTestFiles()
val workingPath = kotlin.io.path.createTempDirectory()
val workingDirFile = workingPath.toFile().also { assert(it.isDirectory) }
try {
val configuration = setupEnvironment()
configuration.put(CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES, listOf(workingDirFile.canonicalPath))
val artifact = compileKlibs(testFiles, configuration, workingDirFile)
val moduleAPaths = artifact.loadKlibFilePaths(denormalize = true)
val dirAPaths = workingDirFile.listFiles { _, name -> name.endsWith(".kt") }!!.map { it.relativeTo(workingDirFile).path }
assertSameElements(dirAPaths, moduleAPaths)
} finally {
workingDirFile.deleteRecursively()
}
}
fun testAbsoluteNormalizedPath() {
val testFiles = createTestFiles()
val workingPath = kotlin.io.path.createTempDirectory()
val workingDirFile = workingPath.toFile().also { assert(it.isDirectory) }
try {
val configuration = setupEnvironment()
configuration.put(CommonConfigurationKeys.KLIB_NORMALIZE_ABSOLUTE_PATH, true)
val artifact = compileKlibs(testFiles, configuration, workingDirFile)
val modulePaths = artifact.loadKlibFilePaths(denormalize = true)
val dirCanonicalPaths = workingDirFile.listFiles { _, name -> name.endsWith(".kt") }!!.map { it.canonicalPath }
assertSameElements(modulePaths, dirCanonicalPaths)
} finally {
workingDirFile.deleteRecursively()
}
}
private fun String.normalizePath(): String = replace(File.separator, "/")
fun testUnrelatedBase() {
val testFiles = createTestFiles()
val workingPath = kotlin.io.path.createTempDirectory()
val dummyPath = kotlin.io.path.createTempDirectory()
val workingDirFile = workingPath.toFile().also { assert(it.isDirectory) }
val dummyFile = dummyPath.toFile().also { assert(it.isDirectory) }
try {
val configuration = setupEnvironment()
configuration.put(CommonConfigurationKeys.KLIB_RELATIVE_PATH_BASES, listOf(dummyFile.canonicalPath))
val artifact = compileKlibs(testFiles, configuration, workingDirFile)
val modulePaths = artifact.loadKlibFilePaths(denormalize = false)
val dirCanonicalPaths = workingDirFile.listFiles { _, name -> name.endsWith(".kt") }!!.map { it.canonicalPath }
assertSameElements(modulePaths.map { it.normalizePath() }, dirCanonicalPaths.map { it.normalizePath() })
} finally {
workingDirFile.deleteRecursively()
dummyFile.deleteRecursively()
}
}
}