如何 运行 测试在内存中编译 kotlin 文件并检查结果?

How do I run tests compiling a kotlin file in memory and check the result?

到目前为止我有

import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler

  MyProjectCompiler.initialize("SampleKtFileOutput")
    .packageName("com.test.sample")
    .compile(File(someFile.path))
    .result { ktSource: String -> K2JVMCompiler()
       .exec(System.out, /** arguments here?*/) }

这会手动启动编译器,但我想在内存中编译第一个编译器(MyProjectCompiler 生成 kotlin 源代码)生成的字符串,并在不写入文件的情况下检查结果。

如果可能,我想包含当前类路径中的所有内容。

看了K2JVMCompilerclass的源码,好像编译器只支持文件编译。深入挖掘,伪造 org.jetbrains.kotlin.codegen.KotlinCodegenFacade 静态方法 compileCorrectFiles.

的条目似乎过于复杂

您最好猜测是使用文件系统来执行此操作。临时 RAM 磁盘可能适合您的需要。 (这是内置的 macOS for example

我发现最简单的方法是使用类似于原始问题中的代码的东西并使用 java.io.tmpdir。这是一个可重复使用的解决方案:

将 kotlin 编译器添加为测试依赖项:

testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-compiler', version: "$kotlin_version"

编译器包装器:

object JvmCompile {

  fun exe(input: File, output: File): Boolean = K2JVMCompiler().run {
    val args = K2JVMCompilerArguments().apply {
      freeArgs = listOf(input.absolutePath)
      loadBuiltInsFromDependencies = true
      destination = output.absolutePath
      classpath = System.getProperty("java.class.path")
          .split(System.getProperty("path.separator"))
          .filter {
            it.asFile().exists() && it.asFile().canRead()
          }.joinToString(":")
      noStdlib = true
      noReflect = true
      skipRuntimeVersionCheck = true
      reportPerf = true
    }
    output.deleteOnExit()
    execImpl(
        PrintingMessageCollector(
            System.out,
            MessageRenderer.WITHOUT_PATHS, true),
        Services.EMPTY,
        args)
  }.code == 0

}

用于从已编译的 classes:

创建对象的类加载器
class Initializer(private val root: File) {

  val loader = URLClassLoader(
      listOf(root.toURI().toURL()).toTypedArray(),
      this::class.java.classLoader)

  @Suppress("UNCHECKED_CAST") 
  inline fun <reified T> loadCompiledObject(clazzName: String): T? 
      = loader.loadClass(clazzName).kotlin.objectInstance as T

  @Suppress("UNCHECKED_CAST") 
  inline fun <reified T> createInstance(clazzName: String): T? 
      = loader.loadClass(clazzName).kotlin.createInstance() as T

}

示例测试用例:

先制作一个kotlin源文件

MockClasswriter("""
    |
    |package com.test
    |
    |class Example : Consumer<String> {
    |  override fun accept(value: String) {
    |    println("found: '$\value'")
    |  }
    |}
    """.trimMargin("|"))
    .writeToFile(codegenOutputFile)

确保它编译:

assertTrue(JvmCompile.exe(codegenOutputFile, compileOutputDir))

加载class作为接口实例

Initializer(compileOutputDir)
      .createInstance<Consumer<String>>("com.test.Example")
      ?.accept("Hello, world!")

输出将符合预期:found: 'Hello, world!'