[K/N] Test for cinterop hints

This commit is contained in:
Sergey Bogolepov
2022-12-15 15:20:37 +02:00
committed by Space Team
parent 233b01f341
commit 64319477f0
4 changed files with 50 additions and 31 deletions
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.konan.blackboxtest
import org.jetbrains.kotlin.konan.blackboxtest.support.* import org.jetbrains.kotlin.konan.blackboxtest.support.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.* import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.* 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.runner.* import org.jetbrains.kotlin.konan.blackboxtest.support.runner.*
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.* import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
import java.io.File import java.io.File
@@ -18,7 +17,7 @@ abstract class AbstractNativeCInteropBaseTest : AbstractNativeSimpleTest() {
internal val targets: KotlinNativeTargets get() = testRunSettings.get<KotlinNativeTargets>() internal val targets: KotlinNativeTargets get() = testRunSettings.get<KotlinNativeTargets>()
internal val kotlinNativeClassLoader: KotlinNativeClassLoader get() = testRunSettings.get<KotlinNativeClassLoader>() internal val kotlinNativeClassLoader: KotlinNativeClassLoader get() = testRunSettings.get<KotlinNativeClassLoader>()
internal fun TestCase.cinteropToLibrary(): TestCompilationResult.Success<out KLIB> { internal fun TestCase.cinteropToLibrary(): TestCompilationResult<out KLIB> {
modules.singleOrNull() modules.singleOrNull()
val compilation = CInteropCompilation( val compilation = CInteropCompilation(
classLoader = testRunSettings.get(), classLoader = testRunSettings.get(),
@@ -27,7 +26,7 @@ abstract class AbstractNativeCInteropBaseTest : AbstractNativeSimpleTest() {
defFile = modules.singleOrNull()!!.files.singleOrNull()!!.location, defFile = modules.singleOrNull()!!.files.singleOrNull()!!.location,
expectedArtifact = toLibraryArtifact() expectedArtifact = toLibraryArtifact()
) )
return compilation.result.assertSuccess() return compilation.result
} }
private fun TestCase.toLibraryArtifact() = KLIB(buildDir.resolve(modules.singleOrNull()!!.name + ".klib")) private fun TestCase.toLibraryArtifact() = KLIB(buildDir.resolve(modules.singleOrNull()!!.name + ".klib"))
@@ -10,6 +10,7 @@ import com.intellij.testFramework.TestDataFile
import org.jetbrains.kotlin.konan.blackboxtest.support.* import org.jetbrains.kotlin.konan.blackboxtest.support.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.* import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.* 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.settings.* import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
import org.jetbrains.kotlin.konan.blackboxtest.support.util.* import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Tag
@@ -35,7 +36,7 @@ abstract class AbstractNativeCInteropKT39120Test : AbstractNativeCInteropBaseTes
val includeFrameworkArgs = listOf("-compiler-option", "-F${testDataDir.canonicalPath}") val includeFrameworkArgs = listOf("-compiler-option", "-F${testDataDir.canonicalPath}")
val test1Case: TestCase = generateCInteropTestCaseWithSingleDef(def1File, includeFrameworkArgs) val test1Case: TestCase = generateCInteropTestCaseWithSingleDef(def1File, includeFrameworkArgs)
val klib1: KLIB = test1Case.cinteropToLibrary().resultingArtifact val klib1: KLIB = test1Case.cinteropToLibrary().assertSuccess().resultingArtifact
val contents1 = klib1.getContents(kotlinNativeClassLoader.classLoader) val contents1 = klib1.getContents(kotlinNativeClassLoader.classLoader)
val expectedFiltered1Output = golden1File.readText() val expectedFiltered1Output = golden1File.readText()
@@ -44,7 +45,7 @@ abstract class AbstractNativeCInteropKT39120Test : AbstractNativeCInteropBaseTes
val cinterop2ExtraArgs = listOf("-l", klib1.klibFile.canonicalPath, "-compiler-option", "-fmodules") val cinterop2ExtraArgs = listOf("-l", klib1.klibFile.canonicalPath, "-compiler-option", "-fmodules")
val test2Case: TestCase = generateCInteropTestCaseWithSingleDef(def2File, includeFrameworkArgs + cinterop2ExtraArgs) val test2Case: TestCase = generateCInteropTestCaseWithSingleDef(def2File, includeFrameworkArgs + cinterop2ExtraArgs)
val klib2: KLIB = test2Case.cinteropToLibrary().resultingArtifact val klib2: KLIB = test2Case.cinteropToLibrary().assertSuccess().resultingArtifact
val contents2 = klib2.getContents(kotlinNativeClassLoader.classLoader) val contents2 = klib2.getContents(kotlinNativeClassLoader.classLoader)
val expectedFiltered2Output = golden2File.readText() val expectedFiltered2Output = golden2File.readText()
@@ -10,12 +10,15 @@ import com.intellij.testFramework.TestDataFile
import org.jetbrains.kotlin.konan.blackboxtest.support.* import org.jetbrains.kotlin.konan.blackboxtest.support.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.* import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.*
import org.jetbrains.kotlin.konan.blackboxtest.support.compilation.TestCompilationArtifact.* 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.runner.* import org.jetbrains.kotlin.konan.blackboxtest.support.runner.*
import org.jetbrains.kotlin.konan.blackboxtest.support.settings.* import org.jetbrains.kotlin.konan.blackboxtest.support.settings.*
import org.jetbrains.kotlin.konan.blackboxtest.support.util.* import org.jetbrains.kotlin.konan.blackboxtest.support.util.*
import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.CInteropHints
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertEquals
import org.jetbrains.kotlin.test.services.JUnit5Assertions.assertTrue
import org.junit.jupiter.api.Assumptions import org.junit.jupiter.api.Assumptions
import org.junit.jupiter.api.Tag import org.junit.jupiter.api.Tag
import java.io.File import java.io.File
@@ -40,8 +43,6 @@ abstract class AbstractNativeCInteropTest : AbstractNativeCInteropBaseTest() {
this is AbstractNativeCInteropFModulesTest && this is AbstractNativeCInteropFModulesTest &&
targets.testTarget.family == Family.ANDROID targets.testTarget.family == Family.ANDROID
) )
Assumptions.assumeFalse(!fmodules && testPath.endsWith("FModules/"))
val testPathFull = getAbsoluteFile(testPath) val testPathFull = getAbsoluteFile(testPath)
val testDataDir = testPathFull.parentFile.parentFile val testDataDir = testPathFull.parentFile.parentFile
val includeFolder = testDataDir.resolve("include") val includeFolder = testDataDir.resolve("include")
@@ -65,11 +66,19 @@ abstract class AbstractNativeCInteropTest : AbstractNativeCInteropBaseTest() {
val testCase: TestCase = generateCInteropTestCaseWithSingleDef(defFile, includeArgs + fmodulesArgs) val testCase: TestCase = generateCInteropTestCaseWithSingleDef(defFile, includeArgs + fmodulesArgs)
val testCompilationResult = testCase.cinteropToLibrary() val testCompilationResult = testCase.cinteropToLibrary()
val klibContents = testCompilationResult.resultingArtifact.getContents(kotlinNativeClassLoader.classLoader) // If we are running fmodules-specific test without -fmodules then we want to be sure that cinterop fails the way we want it to.
if (!fmodules && testPath.endsWith("FModules/")) {
val expectedContents = goldenFile.readText() val loggedData = (testCompilationResult as TestCompilationResult.CompilationToolFailure).loggedData
assertEquals(StringUtilRt.convertLineSeparators(expectedContents), StringUtilRt.convertLineSeparators(klibContents)) { val prettyMessage = CInteropHints.fmodulesHint
"Test failed. CInterop compilation result was: $testCompilationResult" assertTrue(loggedData.toString().contains(prettyMessage)) {
"Test failed. CInterop compilation result was: $testCompilationResult"
}
} else {
val klibContents = testCompilationResult.assertSuccess().resultingArtifact.getContents(kotlinNativeClassLoader.classLoader)
val expectedContents = goldenFile.readText()
assertEquals(StringUtilRt.convertLineSeparators(expectedContents), StringUtilRt.convertLineSeparators(klibContents)) {
"Test failed. CInterop compilation result was: $testCompilationResult"
}
} }
} }
@@ -17,6 +17,7 @@ import java.io.PrintStream
import kotlin.time.Duration import kotlin.time.Duration
import kotlin.time.ExperimentalTime import kotlin.time.ExperimentalTime
import kotlin.time.measureTime import kotlin.time.measureTime
import kotlin.time.measureTimedValue
import org.jetbrains.kotlin.konan.file.File as KonanFile import org.jetbrains.kotlin.konan.file.File as KonanFile
internal data class CompilationToolCallResult( internal data class CompilationToolCallResult(
@@ -73,6 +74,7 @@ internal fun callCompiler(compilerArgs: Array<String>, kotlinNativeClassLoader:
return CompilationToolCallResult(exitCode, compilerOutput, messageCollector.hasErrors(), duration) return CompilationToolCallResult(exitCode, compilerOutput, messageCollector.hasErrors(), duration)
} }
@OptIn(ExperimentalTime::class)
internal fun invokeCInterop( internal fun invokeCInterop(
kotlinNativeClassLoader: ClassLoader, kotlinNativeClassLoader: ClassLoader,
targets: KotlinNativeTargets, targets: KotlinNativeTargets,
@@ -90,30 +92,38 @@ internal fun invokeCInterop(
val interopClass = Class.forName("org.jetbrains.kotlin.native.interop.gen.jvm.Interop", true, kotlinNativeClassLoader) val interopClass = Class.forName("org.jetbrains.kotlin.native.interop.gen.jvm.Interop", true, kotlinNativeClassLoader)
val entryPoint = interopClass.declaredMethods.single { it.name == "interopViaReflection" } val entryPoint = interopClass.declaredMethods.single { it.name == "interopViaReflection" }
val possibleSubsequentCompilerInvocationArgs: Array<String>? val (cinteropResult, duration) = measureTimedValue {
entryPoint.invoke(
@OptIn(ExperimentalTime::class)
val duration = measureTime {
@Suppress("UNCHECKED_CAST")
possibleSubsequentCompilerInvocationArgs = entryPoint.invoke(
interopClass.getDeclaredConstructor().newInstance(), interopClass.getDeclaredConstructor().newInstance(),
"native", "native",
args + extraArgs, args + extraArgs,
false, false,
generatedDir.absolutePath, nativesDir.absolutePath, manifest.path, cstubsName // args for InternalInteropOptions() generatedDir.absolutePath, nativesDir.absolutePath, manifest.path, cstubsName // args for InternalInteropOptions()
) as? Array<String>?
}
// In currently tested usecases, cinterop must return no args for the subsequent compiler call
return if (possibleSubsequentCompilerInvocationArgs == null) {
// TODO There is no technical ability to extract `toolOutput` and `toolOutputHasErrors`
// from C-interop tool invocation at the moment. This should be fixed in the future.
CompilationToolCallResult(exitCode = ExitCode.OK, toolOutput = "", toolOutputHasErrors = false, duration)
} else {
CompilationToolCallResult(
exitCode = ExitCode.COMPILATION_ERROR,
toolOutput = possibleSubsequentCompilerInvocationArgs.joinToString(" "),
toolOutputHasErrors = true,
duration
) )
} }
return when {
// cinterop has failed with a known error that was returned as a result.
cinteropResult is Exception -> {
CompilationToolCallResult(
exitCode = ExitCode.COMPILATION_ERROR,
toolOutput = cinteropResult.message ?: "",
toolOutputHasErrors = true,
duration
)
}
// In currently tested usecases, cinterop must return no args for the subsequent compiler call
cinteropResult is Array<*> -> {
CompilationToolCallResult(
exitCode = ExitCode.COMPILATION_ERROR,
toolOutput = cinteropResult.joinToString(" "),
toolOutputHasErrors = true,
duration
)
}
else -> {
// TODO There is no technical ability to extract `toolOutput` and `toolOutputHasErrors`
// from C-interop tool invocation at the moment. This should be fixed in the future.
CompilationToolCallResult(exitCode = ExitCode.OK, toolOutput = "", toolOutputHasErrors = false, duration)
}
}
} }