Refactor framework tests

This commit is contained in:
Pavel Punegov
2019-12-19 18:51:38 +03:00
committed by Pavel Punegov
parent 1b3504119d
commit 21862ef847
6 changed files with 186 additions and 221 deletions
+76 -166
View File
@@ -365,7 +365,7 @@ Task linkTest(String name, Closure<KonanLinkTest> configureClosure) {
extraOpts task.flags
extraOpts project.globalTestArgs
}
UtilsKt.dependOnKonanBuildingTask(project, lib, target, task)
UtilsKt.dependsOnKonanBuildingTask(task, lib, target)
// Build an executable with library
program(name, targets: [targetName]) {
@@ -397,8 +397,9 @@ Task dynamicTest(String name, Closure<KonanDynamicTest> configureClosure) {
extraOpts task.flags
extraOpts project.globalTestArgs
}
UtilsKt.dependsOnDist(project, "compileKonan${name.capitalize()}${targetName.capitalize()}")
}
def buildTask = UtilsKt.findKonanBuildTask(project, name, target)
UtilsKt.dependsOnDist(buildTask)
}
}
}
@@ -3548,7 +3549,7 @@ Task interopTestBase(String name, boolean multiFile, Closure<KonanInteropTest> c
if (task.enabled) {
konanArtifacts {
def lib = task.interop
UtilsKt.dependOnKonanBuildingTask(project, lib, target, task)
UtilsKt.dependsOnKonanBuildingTask(task, lib, target)
program(name, targets: [target.name]) {
libraries {
@@ -4015,12 +4016,46 @@ standaloneTest("fake_override_0") {
goldValue = "Moved\nMoved\nChild\nSuper\n"
}
Task frameworkTest(String name, Closure<FrameworkTest> configurator) {
return KotlinNativeTestKt.createTest(project, name, FrameworkTest) { task ->
task.configure(configurator)
if (task.enabled) {
konanArtifacts {
task.frameworks.forEach { fr ->
framework(fr.name, targets: [target.name]) {
fr.sources.forEach { src ->
srcFiles src
}
baseDir "$testOutputFramework/${task.testName}"
if (fr.library != null) {
libraries {
file konanArtifacts[fr.library].getArtifactByTarget(target.name)
}
linkerOpts "-L$buildDir"
UtilsKt.dependsOnKonanBuildingTask(task, fr.library, target)
}
if (!useCustomDist) {
dependsOn ":${target.name}CrossDistRuntime", ':distCompiler'
}
extraOpts fr.bitcode ? "-Xembed-bitcode" : "-Xembed-bitcode-marker"
extraOpts fr.opts
extraOpts project.globalTestArgs
artifactName fr.artifact
}
}
}
}
}
}
if (isAppleTarget(project)) {
task testObjCExport(type: FrameworkTest) {
frameworkTest('testObjCExport') {
final String frameworkName = 'Kt'
testName = "ObjCExport"
frameworkNames = [frameworkName]
final String dir = "$testOutputFramework/$testName"
final File lazyHeader = file("$dir/$target-lazy.h")
@@ -4046,8 +4081,8 @@ if (isAppleTarget(project)) {
}
def libraryName = frameworkName + "Library"
konanArtifacts {
def libraryName = frameworkName + "Library"
library(libraryName, targets: [target.name]) {
srcDir "objcexport/library"
artifactName "test-library"
@@ -4059,45 +4094,18 @@ if (isAppleTarget(project)) {
extraOpts "-Xshort-module-name=MyLibrary"
extraOpts "-module-name", "org.jetbrains.kotlin.native.test-library"
}
framework(frameworkName, targets: [target.name]) {
srcFiles fileTree("objcexport") {
include "*.kt"
}
baseDir dir
if (!useCustomDist) {
dependsOn ":${target.name}CrossDistRuntime", ':distCompiler'
}
dependsOn konanArtifacts[libraryName].getByTarget(target.name)
libraries {
file konanArtifacts[libraryName].getArtifactByTarget(target.name)
}
extraOpts "-Xembed-bitcode-marker"
extraOpts "-Xemit-lazy-objc-header=$lazyHeader"
extraOpts project.globalTestArgs
}
}
swiftSources = fileTree('objcexport') {
include '*.swift'
}*.absolutePath
it.createFramework(frameworkName, ['objcexport'], false,
frameworkName, libraryName, ["-Xemit-lazy-objc-header=$lazyHeader"])
swiftSources = ['objcexport']
}
task testObjCExportStatic(type: FrameworkTest) {
frameworkTest('testObjCExportStatic') {
final String frameworkName = 'KtStatic'
final String frameworkArtifactName = 'Kt'
testName = "ObjCExportStatic"
frameworkNames = [frameworkName]
frameworkArtifactNames = [frameworkArtifactName]
codesign = false
final String dir = "$testOutputFramework/$testName"
final String libraryName = frameworkName + "Library"
konanArtifacts {
def libraryName = frameworkName + "Library"
library(libraryName, targets: [target.name]) {
srcDir "objcexport/library"
artifactName "test-library"
@@ -4109,146 +4117,47 @@ if (isAppleTarget(project)) {
extraOpts "-Xshort-module-name=MyLibrary"
extraOpts "-module-name", "org.jetbrains.kotlin.native.test-library"
}
framework(frameworkName, targets: [target.name]) {
srcFiles fileTree("objcexport") {
include "*.kt"
}
baseDir dir
if (!useCustomDist) {
dependsOn ":${target.name}CrossDistRuntime", ':distCompiler'
}
dependsOn konanArtifacts[libraryName].getByTarget(target.name)
libraries {
file konanArtifacts[libraryName].getArtifactByTarget(target.name)
}
extraOpts "-Xembed-bitcode-marker"
extraOpts "-Xstatic-framework"
extraOpts project.globalTestArgs
artifactName frameworkArtifactName
}
}
swiftSources = fileTree('objcexport') {
include '*.swift'
}*.absolutePath
testName = "ObjCExportStatic"
codesign = false
it.createFramework(frameworkName, ['objcexport'], false, frameworkArtifactName, libraryName, ['-Xstatic-framework'])
swiftSources = ['objcexport']
}
task testValuesGenericsFramework(type: FrameworkTest) {
final String frameworkName = 'ValuesGenerics'
testName = frameworkName
frameworkNames = [frameworkName]
konanArtifacts {
framework(frameworkName, targets: [ target ]) {
srcFiles 'objcexport/values.kt'
srcDir 'framework/values_generics'
baseDir "$testOutputFramework/$testName"
if (!useCustomDist) {
dependsOn ":${target}CrossDistRuntime", ':distCompiler'
}
extraOpts "-Xembed-bitcode-marker"
extraOpts project.globalTestArgs
}
}
swiftSources = ['framework/values_generics/values.swift']
frameworkTest('testValuesGenericsFramework') {
testName = 'ValuesGenerics'
it.createFramework('ValuesGenerics', ['objcexport/values.kt', 'framework/values_generics'])
swiftSources = ['framework/values_generics/']
}
task testStdlibFramework(type: FrameworkTest) {
final String frameworkName = 'Stdlib'
testName = frameworkName
frameworkNames = [frameworkName]
frameworkTest("testStdlibFramework") {
testName = 'Stdlib'
it.createFramework('Stdlib', ['framework/stdlib'], true)
if (cacheTesting == null) fullBitcode = true
konanArtifacts {
framework(frameworkName, targets: [target.name]) {
srcDir 'framework/stdlib'
baseDir "$testOutputFramework/$testName"
if (!useCustomDist) {
dependsOn ":${target.name}CrossDistRuntime", ':distCompiler'
}
extraOpts "-Xembed-bitcode"
extraOpts project.globalTestArgs
}
}
swiftSources = ['framework/stdlib/stdlib.swift']
swiftSources = ['framework/stdlib/']
}
if (cacheTesting != null && cacheTesting.isDynamic) {
// testMultipleFrameworks disabled until https://youtrack.jetbrains.com/issue/KT-34262 is fixed.
} else task testMultipleFrameworks(type: FrameworkTest) {
} else frameworkTest("testMultipleFrameworks") {
testName = "MultipleFrameworks"
final String firstFrameworkName = 'First'
final String secondFrameworkName = 'Second'
frameworkNames = [firstFrameworkName, secondFrameworkName]
final String dir = "$testOutputFramework/$testName"
konanArtifacts {
framework(firstFrameworkName, targets: [target.name]) {
srcDir 'framework/multiple/framework1'
srcDir 'framework/multiple/shared'
baseDir dir
if (!useCustomDist) {
dependsOn ":${target.name}CrossDistRuntime", ':distCompiler'
}
extraOpts "-Xembed-bitcode"
extraOpts project.globalTestArgs
}
framework(secondFrameworkName, targets: [target.name]) {
srcDir 'framework/multiple/framework2'
srcDir 'framework/multiple/shared'
baseDir dir
if (!useCustomDist) {
dependsOn ":${target.name}CrossDistRuntime", ':distCompiler'
}
extraOpts "-Xembed-bitcode"
extraOpts project.globalTestArgs
}
}
swiftSources = ['framework/multiple/multiple.swift']
it.createFramework('First',
['framework/multiple/framework1', 'framework/multiple/shared'],
true // bitcode
)
it.createFramework('Second',
['framework/multiple/framework2', 'framework/multiple/shared'],
true // bitcode
)
swiftSources = ['framework/multiple']
}
task testGh3343Framework(type: FrameworkTest) {
final String frameworkName = 'Gh3343'
testName = frameworkName
frameworkNames = [frameworkName]
def mainTask = it
konanArtifacts {
def lib = "objcGh3343"
UtilsKt.dependOnKonanBuildingTask(project, lib, target, mainTask)
framework(frameworkName, targets: [ target ]) {
libraries {
artifact lib
}
srcDir 'framework/gh3343'
baseDir "$testOutputFramework/$testName"
if (!useCustomDist) {
dependsOn ":${target}CrossDistRuntime", ':distCompiler'
}
linkerOpts "-L$buildDir"
extraOpts "-Xembed-bitcode-marker"
extraOpts project.globalTestArgs
}
}
swiftSources = ['framework/gh3343/uselib.swift']
frameworkTest("testGh3343Framework") {
testName = 'Gh3343'
it.createFramework('Gh3343', ['framework/gh3343'], false, 'Gh3343', 'objcGh3343')
swiftSources = ['framework/gh3343/']
}
}
/**
@@ -4453,7 +4362,8 @@ task buildKonanTests { t ->
// Set build dependencies.
dependsOn compileKonanLocalTest
UtilsKt.dependsOnDist(project, "compileKonanLocalTest${target.name.capitalize()}")
def buildTask = UtilsKt.findKonanBuildTask(project, "localTest", target)
UtilsKt.dependsOnDist(buildTask)
// Local tests build into a single binary should depend on this task
project.tasks
@@ -501,7 +501,8 @@ fun KonanTestExecutable.configureXcodeBuild() {
// Create a Frameworks folder inside the build dir.
it += "mkdir -p \"\$TARGET_BUILD_DIR/\$FRAMEWORKS_FOLDER_PATH\""
// Copy each framework to the Frameworks dir.
it += frameworkArtifactNames.map { name ->
it += frameworks.map { framework ->
val name = framework.artifact
"cp -r \"$testOutput/$testName/${project.testTarget.name}/$name.framework\" " +
"\"\$TARGET_BUILD_DIR/\$FRAMEWORKS_FOLDER_PATH/$name.framework\""
}
@@ -4,10 +4,14 @@ import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.Task
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileTree
import org.gradle.api.tasks.Input
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.nio.file.Files
@@ -21,7 +25,7 @@ import java.nio.file.Paths
*
* @property swiftSources Swift-language test sources that use a given framework
* @property testName test name
* @property frameworkNames names of frameworks
* @property frameworks names of frameworks
*/
open class FrameworkTest : DefaultTask(), KonanTestExecutable {
@Input
@@ -31,10 +35,7 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
lateinit var testName: String
@Input
lateinit var frameworkNames: List<String>
@Input
lateinit var frameworkArtifactNames: List<String>
lateinit var frameworks: MutableList<Framework>
@Input
var fullBitcode: Boolean = false
@@ -44,6 +45,56 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
val testOutput: String = project.testOutputFramework
/**
* Framework description.
* @param name is the framework name,
* @param sources framework sources,
* @param bitcode bitcode embedding in the framework,
* @param artifact the name of the resulting artifact,
* @param library library dependency name,
* @param opts additional options for the compiler.
*/
data class Framework(
val name: String,
val sources: List<String>,
val bitcode: Boolean = false,
val artifact: String = name,
val library: String? = null,
val opts: List<String> = emptyList()
)
@JvmOverloads
fun createFramework(name: String,
sources: List<String>,
bitcode: Boolean = false,
artifact: String = name,
library: String? = null,
opts: List<String> = emptyList()
) = Framework(
name,
sources.toFiles(Language.Kotlin).map { it.path },
bitcode, artifact, library, opts
).also {
if (!::frameworks.isInitialized) {
frameworks = mutableListOf(it)
} else {
frameworks.add(it)
}
}
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
it.include("*${this.extension}")
}
fun List<String>.toFiles(language: Language): List<File> =
this.map { language.filesFrom(it) }
.flatMap { it.files }
override val executable: String
get() {
check(::testName.isInitialized) { "Test name should be set" }
@@ -54,38 +105,31 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
override var doBeforeBuild: Action<in Task>? = null
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)
val target = project.testTarget.name
// set crossdist build dependency if custom konan.home wasn't set
if (!(project.property("useCustomDist") as Boolean)) {
setRootDependency("${target}CrossDist", "${target}CrossDistRuntime", "distCompiler")
}
this.dependsOnDist()
// Set Gradle properties for the better navigation
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Kotlin/Native test infrastructure task"
check(::testName.isInitialized) { "Test name should be set" }
check(::frameworkNames.isInitialized) { "Framework names should be set" }
check(::frameworks.isInitialized) { "Frameworks should be set" }
if (!::frameworkArtifactNames.isInitialized) {
frameworkArtifactNames = frameworkNames
}
frameworkNames.forEach { frameworkName ->
val compileTask = project.tasks.getByName("compileKonan$frameworkName")
doBeforeBuild?.let { compileTask.doFirst(it) }
dependsOn(compileTask)
}
// Build test executable as a first action of the task before executing the test
this.doFirst { buildTestExecutable() }
return this
}
private fun setRootDependency(vararg s: String) = s.forEach { dependsOn(project.rootProject.tasks.getByName(it)) }
private fun buildTestExecutable() {
val frameworkParentDirPath = "$testOutput/$testName/${project.testTarget.name}"
frameworkArtifactNames.forEach { frameworkName ->
val frameworkPath = "$frameworkParentDirPath/$frameworkName.framework"
val frameworkBinaryPath = "$frameworkPath/$frameworkName"
frameworks.forEach { framework ->
val frameworkArtifact = framework.artifact
val frameworkPath = "$frameworkParentDirPath/$frameworkArtifact.framework"
val frameworkBinaryPath = "$frameworkPath/$frameworkArtifact"
validateBitcodeEmbedding(frameworkBinaryPath)
if (codesign) codesign(project, frameworkPath)
}
@@ -93,8 +137,8 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
// create a test provider and get main entry point
val provider = Paths.get(testOutput, testName, "provider.swift")
FileWriter(provider.toFile()).use { writer ->
val providers = swiftSources
.map { Paths.get(it).fileName.toString().removeSuffix(".swift").capitalize() }
val providers = swiftSources.toFiles(Language.Swift)
.map { it.name.toString().removeSuffix(".swift").capitalize() }
.map { "${it}Tests" }
writer.write("""
@@ -109,8 +153,8 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
val swiftMain = Paths.get(testHome.toString(), "main.swift").toString()
// Compile swift sources
val sources = swiftSources.map { Paths.get(it).toString() } +
listOf(provider.toString(), swiftMain)
val sources = swiftSources.toFiles(Language.Swift)
.map { it.path } + listOf(provider.toString(), swiftMain)
val options = listOf(
"-g",
"-Xlinker", "-rpath", "-Xlinker", "@executable_path/Frameworks",
@@ -123,6 +167,8 @@ open class FrameworkTest : DefaultTask(), KonanTestExecutable {
@TaskAction
fun run() {
// Build test executable as a first action of the task before executing the test
buildTestExecutable()
doBeforeRun?.execute(this)
runTest(executorService = project.executor, testExecutable = Paths.get(executable))
}
@@ -25,4 +25,9 @@ interface KonanTestExecutable : Task {
* or as its `doFirst` action.
*/
var doBeforeBuild: Action<in Task>?
/**
* Build tasks that this [executable] depends on, or is built from.
*/
val buildTasks: List<Task>
}
@@ -78,6 +78,9 @@ abstract class KonanTest : DefaultTask(), KonanTestExecutable {
@Input @Optional
override var doBeforeRun: Action<in Task>? = null
override val buildTasks: List<Task>
get() = listOf(project.findKonanBuildTask(name, project.testTarget))
@Suppress("UnstableApiUsage")
override fun configure(config: Closure<*>): Task {
super.configure(config)
@@ -117,20 +120,19 @@ abstract class KonanTest : DefaultTask(), KonanTestExecutable {
/**
* Create a test task of the given type. Supports configuration with Closure passed form build.gradle file.
*/
fun <T: KonanTest> Project.createTest(name: String, type: Class<T>, config: Closure<*>): T =
fun <T: KonanTestExecutable> Project.createTest(name: String, type: Class<T>, config: Closure<*>): T =
project.tasks.create(name, type).apply {
// Apply closure set in build.gradle to get all parameters.
this.configure(config)
if (enabled) {
// Configure test task.
val target = project.testTarget
// If run task depends on something, compile task should also depend on this.
val compileTask = project.tasks.getByName("compileKonan${name.capitalize()}${target.name.capitalize()}")
compileTask.sameDependenciesAs(this)
// Run task should depend on compile task
this.dependsOn(compileTask)
doBeforeBuild?.let { compileTask.doFirst(it) }
compileTask.enabled = enabled
// If run task depends on something, build tasks should also depend on this.
buildTasks.forEach { buildTask ->
buildTask.sameDependenciesAs(this)
// Run task should depend on compile task
this.dependsOn(buildTask)
doBeforeBuild?.let { buildTask.doFirst(it) }
buildTask.enabled = enabled
}
}
}
@@ -177,7 +179,7 @@ open class KonanGTest : KonanTest() {
.apply { if (find()) fail(group(1).toInt()) }
if (total == 0) {
// No test were run. Try to find if we've tried to run something
this.error(Pattern.compile("\\[={10}] Running ([0-9]*) tests from ([0-9]*) test cases\\..*")
error(Pattern.compile("\\[={10}] Running ([0-9]*) tests from ([0-9]*) test cases\\..*")
.matcher(output)
.run { if (find()) group(1).toInt() else 1 })
}
@@ -12,8 +12,6 @@ import org.jetbrains.kotlin.konan.properties.propertyList
import org.jetbrains.kotlin.konan.properties.saveProperties
import org.jetbrains.kotlin.konan.target.*
import org.jetbrains.kotlin.library.KLIB_PROPERTY_NATIVE_TARGETS
import java.io.FileInputStream
import java.io.IOException
import java.io.File
import java.util.concurrent.TimeUnit
import java.net.HttpURLConnection
@@ -99,6 +97,9 @@ fun Project.getFilesToCompile(compile: List<String>, exclude: List<String>): Lis
//region Task dependency.
fun Project.findKonanBuildTask(artifact: String, target: KonanTarget): Task =
tasks.getByName("compileKonan${artifact.capitalize()}${target.name.capitalize()}")
fun Project.dependsOnDist(taskName: String) {
project.tasks.getByName(taskName).dependsOnDist()
}
@@ -111,7 +112,7 @@ fun Task.dependsOnDist() {
val target = project.testTarget
if (target != HostManager.host) {
// if a test_target property is set then tests should depend on a crossDist
// otherwise runtime components would not be build for a target.
// otherwise, runtime components would not be build for a target.
dependsOn(rootTasks.getByName("${target.name}CrossDist"))
}
}
@@ -134,14 +135,14 @@ fun Task.sameDependenciesAs(task: Task) {
}
/**
* Set dependency on [lib] built by the Konan Plugin for the [task],
* also make [lib] depend on `dist` and all dependencies of the [task] to make [lib] execute before the [task].
* Set dependency on [artifact] built by the Konan Plugin for the receiver task,
* also make [artifact] depend on `dist` and all dependencies of the task to make [artifact] execute before the task.
*/
fun Project.dependOnKonanBuildingTask(lib: String, target: KonanTarget, task: Task) {
val libTask = "compileKonan${lib.capitalize()}${target.name.capitalize()}"
this.dependsOnDist(libTask)
libTask.sameDependenciesAs(task)
task.dependsOn(libTask)
fun Task.dependsOnKonanBuildingTask(artifact: String, target: KonanTarget) {
val buildTask = project.findKonanBuildTask(artifact, target)
buildTask.dependsOnDist()
buildTask.sameDependenciesAs(this)
dependsOn(buildTask)
}
//endregion
@@ -210,7 +211,7 @@ fun getBuild(buildLocator: String, user: String, password: String) =
fun sendGetRequest(url: String, username: String? = null, password: String? = null) : String {
val connection = URL(url).openConnection() as HttpURLConnection
if (username != null && password != null) {
val auth = Base64.getEncoder().encode((username + ":" + password).toByteArray()).toString(Charsets.UTF_8)
val auth = Base64.getEncoder().encode(("$username:$password").toByteArray()).toString(Charsets.UTF_8)
connection.addRequestProperty("Authorization", "Basic $auth")
}
connection.setRequestProperty("Accept", "application/json");
@@ -318,4 +319,4 @@ fun Project.mergeManifestsByTargets(source: File, destination: File) {
destinationProperties[KLIB_PROPERTY_NATIVE_TARGETS] = mergedNativeTargets.joinToString(" ")
destinationFile.saveProperties(destinationProperties)
}
}