6b409d87f5
This change adds a library with cinterop that has XCTest wrapper around Kotlin/Native tests (that are @kotlin.test.Test marked methods). This library can be compiled with either test code using the option `-produce test_bundle` to make a loadable test bundle or used inside the existing ObjC/Swift tests if compiled to a framework. The basic idea is to make XCTest be able to resolve separate test cases and correctly show them in test reports. This was achieved by wrapping test cases with dynamically created invocation methods. Test listeners are integrated with XCTest Observation to make it possible to have the same ability to report with GTest or TeamCity logging. Gradle build files use MPP Gradle plugin and use a bootstrap version of K/N. Property `kotlin.native.home` was moved to the kotlin-native subproject to not override this project's K/N distribution, that is being used by the KGP with the same property. This is a part of ^KT-58928 Merge-request: KT-MR-13268 Merged-by: Pavel Punegov <Pavel.Punegov@jetbrains.com>
146 lines
5.3 KiB
Kotlin
146 lines
5.3 KiB
Kotlin
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
|
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
|
|
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
|
|
import org.jetbrains.kotlin.gradle.tasks.CInteropProcess
|
|
import org.jetbrains.kotlin.konan.target.*
|
|
import java.io.ByteArrayOutputStream
|
|
import java.nio.file.Paths
|
|
|
|
description = "XCTest wrapper of Native kotlin.test"
|
|
|
|
plugins {
|
|
kotlin("multiplatform")
|
|
}
|
|
|
|
/**
|
|
* Path to the target SDK platform.
|
|
*
|
|
* By default, K/N includes only SDK frameworks as platform libs.
|
|
* It's required to get the Library frameworks path where the `XCTest.framework` is located.
|
|
* Consider making XCTest a platform lib with KT-61709.
|
|
*/
|
|
fun targetPlatform(target: String): String {
|
|
val out = ByteArrayOutputStream()
|
|
val result = project.exec {
|
|
executable = "/usr/bin/xcrun"
|
|
args = listOf("--sdk", target, "--show-sdk-platform-path")
|
|
standardOutput = out
|
|
}
|
|
check(result.exitValue == 0) {
|
|
"xcrun failed with ${result.exitValue}. See the output: $out"
|
|
}
|
|
|
|
return out.toString().trim()
|
|
}
|
|
|
|
/**
|
|
* Returns a path to the Xcode developer frameworks location based on the specified KonanTarget.
|
|
*/
|
|
fun KonanTarget.developerFrameworkPath(): String {
|
|
val platform = when (this) {
|
|
KonanTarget.MACOS_ARM64, KonanTarget.MACOS_X64 -> "macosx"
|
|
KonanTarget.IOS_SIMULATOR_ARM64, KonanTarget.IOS_X64 -> "iphonesimulator"
|
|
KonanTarget.IOS_ARM64 -> "iphoneos"
|
|
else -> error("Target $this is not supported here")
|
|
}
|
|
|
|
return targetPlatform(platform) + "/Developer/Library/Frameworks/"
|
|
}
|
|
|
|
/**
|
|
* Registers a task to copy the XCTest framework to the build directory for the specified KonanTarget.
|
|
*
|
|
* @param target The KonanTarget for which the copy framework task should be registered.
|
|
* @return The TaskProvider representing the registered copy framework task.
|
|
*/
|
|
fun registerCopyFrameworkTask(target: KonanTarget): TaskProvider<Sync> =
|
|
tasks.register<Sync>("${target}FrameworkCopy") {
|
|
val devFrameworkPath = Paths.get(target.developerFrameworkPath())
|
|
check(devFrameworkPath.toFile().exists()) {
|
|
"Developer frameworks path wasn't found at $devFrameworkPath. Check configuration and Xcode installation"
|
|
}
|
|
|
|
from(devFrameworkPath.resolve("XCTest.framework"))
|
|
into(layout.buildDirectory.dir("$target/Frameworks/XCTest.framework"))
|
|
}
|
|
|
|
val nativeTargets = mutableListOf<KotlinNativeTarget>()
|
|
|
|
if (HostManager.hostIsMac) {
|
|
kotlin {
|
|
with(nativeTargets) {
|
|
add(macosX64())
|
|
add(macosArm64())
|
|
add(iosX64())
|
|
add(iosArm64())
|
|
add(iosSimulatorArm64())
|
|
|
|
forEach {
|
|
val copyTask = registerCopyFrameworkTask(it.konanTarget)
|
|
it.compilations.all {
|
|
cinterops {
|
|
register("XCTest") {
|
|
compilerOpts(
|
|
"-iframework", project.layout.buildDirectory
|
|
.dir("$konanTarget/Frameworks")
|
|
.get()
|
|
.asFile
|
|
.absolutePath
|
|
)
|
|
// cinterop task should depend on the framework copy task
|
|
tasks.named(interopProcessingTaskName).configure {
|
|
dependsOn(copyTask)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sourceSets.all {
|
|
languageSettings.apply {
|
|
// Oh, yeah! So much experimental, so wow!
|
|
optIn("kotlinx.cinterop.BetaInteropApi")
|
|
optIn("kotlinx.cinterop.ExperimentalForeignApi")
|
|
optIn("kotlin.experimental.ExperimentalNativeApi")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
val kotlinTestNativeXCTest by configurations.creating {
|
|
attributes {
|
|
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
|
|
attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
|
|
}
|
|
}
|
|
|
|
nativeTargets.forEach { target ->
|
|
val targetName = target.konanTarget.name
|
|
val mainCompilation = target.compilations.getByName("main")
|
|
val outputKlibTask = mainCompilation.compileTaskProvider
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
val cinteropKlibTask = tasks.named(
|
|
mainCompilation.cinterops
|
|
.getByName("XCTest")
|
|
.interopProcessingTaskName
|
|
) as? TaskProvider<CInteropProcess> ?: error("Unable to get CInteropProcess task provider")
|
|
|
|
val frameworkCopyTask = tasks.named<Sync>("${targetName}FrameworkCopy")
|
|
|
|
artifacts {
|
|
add(kotlinTestNativeXCTest.name, outputKlibTask.flatMap { it.outputFile }) {
|
|
classifier = targetName
|
|
builtBy(outputKlibTask)
|
|
}
|
|
add(kotlinTestNativeXCTest.name, cinteropKlibTask.flatMap { it.outputFileProvider }) {
|
|
classifier = targetName
|
|
builtBy(cinteropKlibTask)
|
|
}
|
|
// Add a path to a directory that contains copied framework to share it with test infrastructure
|
|
add(kotlinTestNativeXCTest.name, frameworkCopyTask.map { it.destinationDir.parentFile }) {
|
|
classifier = "${targetName}Frameworks"
|
|
builtBy(frameworkCopyTask)
|
|
}
|
|
}
|
|
} |