[K/N][Tests] Clean up framework testing code
^KT-61259
This commit is contained in:
committed by
Space Team
parent
c72443e4be
commit
95b1fba37f
@@ -177,15 +177,9 @@ tasks.named("clean", Delete.class) {
|
|||||||
|
|
||||||
tasks.named("run") {
|
tasks.named("run") {
|
||||||
dependsOn(tasks.withType(KonanTest).matching { it.enabled })
|
dependsOn(tasks.withType(KonanTest).matching { it.enabled })
|
||||||
// Add framework tests
|
|
||||||
dependsOn(tasks.withType(FrameworkTest).matching { it.enabled })
|
|
||||||
dependsOn(tasks.withType(FileCheckTest).matching { it.enabled })
|
dependsOn(tasks.withType(FileCheckTest).matching { it.enabled })
|
||||||
dependsOn(tasks.withType(KonanTest).matching { it.enabled })
|
|
||||||
// Add framework tests
|
|
||||||
dependsOn(tasks.withType(FrameworkTest).matching { it.enabled })
|
|
||||||
// Add regular gradle test tasks
|
// Add regular gradle test tasks
|
||||||
dependsOn(tasks.withType(Test).matching { it.enabled })
|
dependsOn(tasks.withType(Test).matching { it.enabled })
|
||||||
dependsOn(tasks.withType(FileCheckTest).matching { it.enabled })
|
|
||||||
dependsOn(":kotlin-native:Interop:Indexer:check")
|
dependsOn(":kotlin-native:Interop:Indexer:check")
|
||||||
dependsOn(":kotlin-native:Interop:StubGenerator:check")
|
dependsOn(":kotlin-native:Interop:StubGenerator:check")
|
||||||
dependsOn(":native:kotlin-native-utils:check")
|
dependsOn(":native:kotlin-native-utils:check")
|
||||||
@@ -1836,42 +1830,6 @@ standaloneTest("split_compilation_pipeline") {
|
|||||||
useGoldenData = true
|
useGoldenData = true
|
||||||
}
|
}
|
||||||
|
|
||||||
Task frameworkTest(String name, Closure<FrameworkTest> configurator) {
|
|
||||||
return KotlinNativeTestKt.createTest(project, name, FrameworkTest) { task ->
|
|
||||||
configurator.delegate = task
|
|
||||||
UtilsKt.dependsOnDist(task)
|
|
||||||
configurator()
|
|
||||||
if (task.enabled) {
|
|
||||||
konanArtifacts {
|
|
||||||
task.frameworks.forEach { fr ->
|
|
||||||
framework(fr.name, targets: [target.name]) {
|
|
||||||
fr.sources.forEach { src ->
|
|
||||||
srcFiles src
|
|
||||||
}
|
|
||||||
baseDir "$testOutputFramework/${task.name}"
|
|
||||||
|
|
||||||
fr.libraries.forEach { library ->
|
|
||||||
dependsOn konanArtifacts[library].getByTarget(target.name)
|
|
||||||
libraries {
|
|
||||||
klibFile konanArtifacts[library].getArtifactByTarget(target.name)
|
|
||||||
}
|
|
||||||
linkerOpts "-L${project.layout.buildDirectory.get().asFile}"
|
|
||||||
UtilsKt.dependsOnKonanBuildingTask(task, library, target)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fr.isStatic) extraOpts "-Xstatic-framework"
|
|
||||||
extraOpts fr.opts
|
|
||||||
extraOpts project.globalTestArgs
|
|
||||||
|
|
||||||
artifactName fr.artifact
|
|
||||||
}
|
|
||||||
UtilsKt.dependsOnDist(project.tasks.findByName("compileKonan${fr.name.capitalize()}${UtilsKt.getTestTargetSuffix(project)}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.register("override_konan_properties0", KonanDriverTest) {
|
tasks.register("override_konan_properties0", KonanDriverTest) {
|
||||||
disabled = isAggressiveGC // No need to test with different GC schedulers
|
disabled = isAggressiveGC // No need to test with different GC schedulers
|
||||||
def overrides = PlatformInfo.isWindows()
|
def overrides = PlatformInfo.isWindows()
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
package org.jetbrains.kotlin
|
|
||||||
|
|
||||||
import groovy.lang.Closure
|
|
||||||
import org.gradle.api.Action
|
|
||||||
import org.gradle.api.DefaultTask
|
|
||||||
import org.gradle.api.Task
|
|
||||||
import org.gradle.api.file.FileTree
|
|
||||||
import org.gradle.api.tasks.Input
|
|
||||||
import org.gradle.api.tasks.Internal
|
|
||||||
import org.gradle.api.tasks.Optional
|
|
||||||
import org.gradle.api.tasks.TaskAction
|
|
||||||
import org.gradle.language.base.plugins.LifecycleBasePlugin
|
|
||||||
|
|
||||||
import org.jetbrains.kotlin.konan.target.*
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileWriter
|
|
||||||
import java.io.Serializable
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.nio.file.Paths
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test task for -produce framework testing. Requires a framework to be built by the Konan plugin
|
|
||||||
* with konanArtifacts { framework(frameworkName, targets: [ testTarget] ) } and a dependency set
|
|
||||||
* according to a pattern "compileKonan${frameworkName}".
|
|
||||||
*
|
|
||||||
* @property swiftSources Swift-language test sources that use a given framework
|
|
||||||
* @property frameworks names of frameworks
|
|
||||||
*/
|
|
||||||
open class FrameworkTest : DefaultTask(), KonanTestExecutable {
|
|
||||||
@Input
|
|
||||||
lateinit var swiftSources: List<String>
|
|
||||||
|
|
||||||
@Input
|
|
||||||
var swiftExtraOpts: List<String> = emptyList()
|
|
||||||
|
|
||||||
@Input
|
|
||||||
lateinit var frameworks: MutableList<Framework>
|
|
||||||
|
|
||||||
@Input
|
|
||||||
var codesign: Boolean = true
|
|
||||||
|
|
||||||
@Input
|
|
||||||
val testOutput: String = project.testOutputFramework
|
|
||||||
|
|
||||||
@Input @Optional
|
|
||||||
var expectedExitStatus: Int? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Framework description.
|
|
||||||
*
|
|
||||||
* @param name is the framework name,
|
|
||||||
* @param sources framework sources,
|
|
||||||
* @param isStatic determines that framework is static
|
|
||||||
* @param artifact the name of the resulting artifact,
|
|
||||||
* @param library list of library dependency names,
|
|
||||||
* @param opts additional options for the compiler.
|
|
||||||
*/
|
|
||||||
class Framework(
|
|
||||||
val name: String,
|
|
||||||
var sources: List<String> = emptyList(),
|
|
||||||
var isStatic: Boolean = false,
|
|
||||||
var artifact: String = name,
|
|
||||||
var libraries: List<String> = emptyList(),
|
|
||||||
var opts: List<String> = emptyList()
|
|
||||||
) : Serializable // Required for Gradle when using Framework as task input.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for the framework configuration in the task's closure.
|
|
||||||
*/
|
|
||||||
fun framework(name: String, closure: Closure<Framework>): Framework {
|
|
||||||
val f = Framework(name).apply {
|
|
||||||
closure.delegate = this
|
|
||||||
closure.resolveStrategy = Closure.DELEGATE_FIRST
|
|
||||||
closure.call()
|
|
||||||
// map to file paths
|
|
||||||
sources = sources.toFiles(Language.Kotlin).map { it.path }
|
|
||||||
}
|
|
||||||
if (!::frameworks.isInitialized) {
|
|
||||||
frameworks = mutableListOf(f)
|
|
||||||
} else {
|
|
||||||
frameworks.add(f)
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Language(val extension: String) {
|
|
||||||
Kotlin(".kt"), ObjC(".m"), Swift(".swift")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Language.filesFrom(dir: String): FileTree = project.fileTree(dir) {
|
|
||||||
// include only files with the language extension
|
|
||||||
include("*${this@filesFrom.extension}")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun List<String>.toFiles(language: Language): List<File> =
|
|
||||||
this.map { language.filesFrom(it) }
|
|
||||||
.flatMap { it.files }
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
override val executable: String
|
|
||||||
get() = Paths.get(testOutput, name, "swiftTestExecutable").toString()
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
override var doBeforeRun: Action<in Task>? = null
|
|
||||||
|
|
||||||
@Internal
|
|
||||||
override var doBeforeBuild: Action<in Task>? = null
|
|
||||||
|
|
||||||
@get:Internal
|
|
||||||
override val buildTasks: List<Task>
|
|
||||||
get() = frameworks.map { project.tasks.getByName("compileKonan${it.name}") }
|
|
||||||
|
|
||||||
@Suppress("UnstableApiUsage")
|
|
||||||
override fun configure(config: Closure<*>): Task {
|
|
||||||
super.configure(config)
|
|
||||||
// set crossdist build dependency if custom konan.home wasn't set
|
|
||||||
this.dependsOnDist()
|
|
||||||
|
|
||||||
// Set Gradle properties for the better navigation
|
|
||||||
group = LifecycleBasePlugin.VERIFICATION_GROUP
|
|
||||||
description = "Kotlin/Native test infrastructure task"
|
|
||||||
|
|
||||||
check(::frameworks.isInitialized) { "Frameworks should be set" }
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildTestExecutable() {
|
|
||||||
val frameworkParentDirPath = "$testOutput/$name/${project.testTarget.name}"
|
|
||||||
frameworks.forEach { framework ->
|
|
||||||
val frameworkArtifact = framework.artifact
|
|
||||||
val frameworkPath = "$frameworkParentDirPath/$frameworkArtifact.framework"
|
|
||||||
if (codesign) codesign(project, frameworkPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a test provider and get main entry point
|
|
||||||
val provider = Paths.get(testOutput, name, "provider.swift")
|
|
||||||
FileWriter(provider.toFile()).use { writer ->
|
|
||||||
val providers = swiftSources.toFiles(Language.Swift)
|
|
||||||
.map { file ->
|
|
||||||
file.name.toString().removeSuffix(".swift").replaceFirstChar { it.uppercase() }
|
|
||||||
}
|
|
||||||
.map { "${it}Tests" }
|
|
||||||
|
|
||||||
writer.write("""
|
|
||||||
|// THIS IS AUTOGENERATED FILE
|
|
||||||
|// This method is invoked by the main routine to get a list of tests
|
|
||||||
|func registerProviders() {
|
|
||||||
| ${providers.joinToString("\n ") { "$it()" }}
|
|
||||||
|}
|
|
||||||
""".trimMargin())
|
|
||||||
}
|
|
||||||
val testHome = project.file("framework").toPath()
|
|
||||||
val swiftMain = Paths.get(testHome.toString(), "main.swift").toString()
|
|
||||||
|
|
||||||
// Compile swift sources
|
|
||||||
val sources = swiftSources.toFiles(Language.Swift)
|
|
||||||
.map { it.path } + listOf(provider.toString(), swiftMain)
|
|
||||||
val options = listOf(
|
|
||||||
"-g",
|
|
||||||
"-Xlinker", "-rpath", "-Xlinker", "@executable_path/Frameworks",
|
|
||||||
"-Xlinker", "-rpath", "-Xlinker", frameworkParentDirPath,
|
|
||||||
"-F", frameworkParentDirPath,
|
|
||||||
"-Xcc", "-Werror" // To fail compilation on warnings in framework header.
|
|
||||||
)
|
|
||||||
// As of Xcode 13.1 swift passes wrong libclang_rt to simulator targets (similar to KT-47333).
|
|
||||||
// To workaround this problem, we explicitly provide the correct one.
|
|
||||||
val simulatorHack = if (project.testTargetConfigurables.targetTriple.isSimulator) {
|
|
||||||
project.platformManager.platform(project.testTarget).linker.provideCompilerRtLibrary("")?.let {
|
|
||||||
listOf("-Xlinker", it)
|
|
||||||
} ?: emptyList()
|
|
||||||
} else {
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
compileSwift(project, project.testTarget, sources, options + simulatorHack + swiftExtraOpts, Paths.get(executable))
|
|
||||||
}
|
|
||||||
|
|
||||||
@TaskAction
|
|
||||||
fun run() {
|
|
||||||
// Build test executable as a first action of the task before executing the test
|
|
||||||
buildTestExecutable()
|
|
||||||
doBeforeRun?.execute(this)
|
|
||||||
val testExecutable = Paths.get(executable)
|
|
||||||
val (stdOut, stdErr, exitCode) = runProcess(
|
|
||||||
executor = {
|
|
||||||
project.executor.execute {
|
|
||||||
it.execute(this)
|
|
||||||
workingDir = Paths.get(testOutput).toFile()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
executable = testExecutable.toString())
|
|
||||||
|
|
||||||
val testExecName = testExecutable.fileName
|
|
||||||
println("""
|
|
||||||
|$testExecName
|
|
||||||
|stdout: $stdOut
|
|
||||||
|stderr: $stdErr
|
|
||||||
""".trimMargin())
|
|
||||||
val timeoutMessage = if (exitCode == -1) {
|
|
||||||
"WARNING: probably a timeout\n"
|
|
||||||
} else ""
|
|
||||||
val expectedExitCode = expectedExitStatus?.takeUnless { project.compileOnlyTests } ?: 0
|
|
||||||
check(exitCode == expectedExitCode) { "${timeoutMessage}Execution of $testExecName failed with exit code: $exitCode (expected exit code: $expectedExitCode)" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+4
-6
@@ -189,6 +189,8 @@ internal fun invokeSwiftC(
|
|||||||
File(configs.absoluteTargetToolchain).resolveSibling("ExtraFrameworks").absolutePath
|
File(configs.absoluteTargetToolchain).resolveSibling("ExtraFrameworks").absolutePath
|
||||||
}
|
}
|
||||||
} catch (rpe: RunProcessException) {
|
} catch (rpe: RunProcessException) {
|
||||||
|
if (rpe.exitCode == null)
|
||||||
|
throw rpe // Treat compiler timeouts as fatal errors
|
||||||
return CompilationToolCallResult(
|
return CompilationToolCallResult(
|
||||||
exitCode = ExitCode.COMPILATION_ERROR,
|
exitCode = ExitCode.COMPILATION_ERROR,
|
||||||
toolOutput = "ARGS: $args\nSTDOUT: ${rpe.stdout}\nSTDERR: ${rpe.stderr}",
|
toolOutput = "ARGS: $args\nSTDOUT: ${rpe.stdout}\nSTDERR: ${rpe.stderr}",
|
||||||
@@ -207,11 +209,7 @@ internal fun invokeSwiftC(
|
|||||||
internal fun codesign(path: String) {
|
internal fun codesign(path: String) {
|
||||||
val executableAbsolutePath = "/usr/bin/codesign"
|
val executableAbsolutePath = "/usr/bin/codesign"
|
||||||
val args = arrayOf("--verbose", "-s", "-", path)
|
val args = arrayOf("--verbose", "-s", "-", path)
|
||||||
try {
|
runProcess(executableAbsolutePath, *args) {
|
||||||
runProcess(executableAbsolutePath, *args) {
|
timeout = Duration.parse("30s")
|
||||||
timeout = Duration.parse("30s")
|
|
||||||
}
|
|
||||||
} catch (rpe: RunProcessException) {
|
|
||||||
throw AssertionError("`$executableAbsolutePath ${args.joinToString(" ")}` failed with exitCode=${rpe.exitCode}: ${rpe.message}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user