Как запустить тесты, компилирующие файл kotlin в памяти и проверить результат?

пока у меня есть

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) в памяти и проверяет результат без записи в файл.

Я хотел бы включить все в текущий путь к классам, если это возможно.

2 ответов


я нашел самый простой способ сделать это-использовать что-то вроде кода в исходный вопрос и использовать 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

}

classloader для создания объектов из скомпилированных классов:

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)

убедитесь, что он compiles:

assertTrue(JvmCompile.exe(codegenOutputFile, compileOutputDir))

загрузите класс как экземпляр интерфейса

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

выход будет, как ожидалось:found: 'Hello, world!'


Читать источник K2JVMCompiler class, похоже, что компилятор поддерживает компиляцию только для файлов. Копая глубже, кажется, слишком сложно подделать записи org.jetbrains.kotlin.codegen.KotlinCodegenFacade статический метод compileCorrectFiles.

лучше использовать файловую систему для этого. Временный диск может удовлетворить ваши потребности. (Это встроенный macOS )