Execute tests with iOS simulator
This commit is contained in:
committed by
Pavel Punegov
parent
68fe66a21f
commit
3645c22f8d
@@ -66,7 +66,9 @@ externalTestsDir.mkdirs()
|
||||
|
||||
ext.platformManager = project.rootProject.platformManager
|
||||
ext.target = platformManager.targetManager(project.testTarget).target
|
||||
project.convention.plugins.remoteExec = new org.jetbrains.kotlin.ExecRemote(project)
|
||||
|
||||
// Add executor to run tests depending on a target
|
||||
project.convention.plugins.executor = ExecutorServiceKt.create(project)
|
||||
|
||||
allprojects {
|
||||
// Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set.
|
||||
@@ -2529,7 +2531,8 @@ if (isMac()) {
|
||||
if (!useCustomDist) {
|
||||
dependsOn ':distPlatformLibs'
|
||||
}
|
||||
disabled = (project.testTarget == 'wasm32') // No interop for wasm yet.
|
||||
disabled = (project.testTarget == 'wasm32') || // No interop for wasm yet.
|
||||
(project.testTarget == 'ios_x64') // No GLUT in iOS
|
||||
run = false
|
||||
source = "interop/basics/opengl_teapot.kt"
|
||||
}
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin
|
||||
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.process.ExecResult
|
||||
import org.gradle.process.ExecSpec
|
||||
import org.gradle.util.ConfigureUtil
|
||||
|
||||
import java.nio.file.Paths
|
||||
import java.util.function.Function
|
||||
|
||||
/**
|
||||
* This class provides process.execRemote -- a drop-in replacement for process.exec.
|
||||
* If we provide -Premote=user@host the binaries are executed on a remote host
|
||||
* If we omit -Premote then the binary is executed locally as usual.
|
||||
*/
|
||||
class ExecRemote {
|
||||
|
||||
private final Project project
|
||||
private final Function<Action<? super ExecSpec>, ExecResult> executor
|
||||
|
||||
ExecRemote(Project project) {
|
||||
this.project = project
|
||||
if (project.hasProperty('remote')) {
|
||||
def remote = project.property('remote') as String
|
||||
executor = new SSHExecutor(remote)
|
||||
} else {
|
||||
executor = { project.exec(it) }
|
||||
}
|
||||
}
|
||||
|
||||
ExecResult execRemote(Closure<? super ExecSpec> closure) {
|
||||
this.execRemote(ConfigureUtil.configureUsing(closure))
|
||||
}
|
||||
|
||||
ExecResult execRemote(Action<? super ExecSpec> action) {
|
||||
this.executor.apply(action)
|
||||
}
|
||||
|
||||
private class SSHExecutor implements Function<Action<? super ExecSpec>, ExecResult> {
|
||||
private final String remote
|
||||
private final String remoteDir
|
||||
|
||||
SSHExecutor(String remote) {
|
||||
this.remote = remote
|
||||
this.remoteDir = uniqueSessionName()
|
||||
}
|
||||
|
||||
@Override
|
||||
ExecResult apply(Action<? super ExecSpec> action) {
|
||||
String execFile
|
||||
|
||||
createRemoteDir()
|
||||
def execResult = project.exec { ExecSpec execSpec ->
|
||||
action.execute(execSpec)
|
||||
execSpec.with {
|
||||
upload(executable)
|
||||
execFile = executable = "$remoteDir/${new File(executable).name}"
|
||||
commandLine = ['/usr/bin/ssh', remote] + commandLine
|
||||
}
|
||||
}
|
||||
cleanup(execFile)
|
||||
return execResult
|
||||
}
|
||||
|
||||
private String uniqueSessionName() {
|
||||
def date = new Date().format('yyyyMMddHHmmss')
|
||||
Paths.get(project.ext.remoteRoot.toString(), "tmp",
|
||||
System.properties['user.name'].toString() + "_" + date).toString()
|
||||
}
|
||||
|
||||
private createRemoteDir() {
|
||||
project.exec {
|
||||
commandLine('ssh', remote, 'mkdir', '-p', remoteDir)
|
||||
}
|
||||
}
|
||||
|
||||
private upload(String fileName) {
|
||||
project.exec {
|
||||
commandLine('scp', fileName, "${remote}:${remoteDir}")
|
||||
}
|
||||
}
|
||||
|
||||
private cleanup(String fileName) {
|
||||
project.exec {
|
||||
commandLine('ssh', remote, 'rm', fileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,9 +237,9 @@ fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation<Any?> = ob
|
||||
|
||||
def out = new ByteArrayOutputStream()
|
||||
//TODO Add test timeout
|
||||
ExecResult execResult = project.execRemote {
|
||||
ExecResult execResult = project.execute {
|
||||
|
||||
commandLine executionCommandLine(exe)
|
||||
commandLine exe
|
||||
|
||||
if (arguments != null) {
|
||||
args arguments
|
||||
@@ -277,23 +277,6 @@ fun handleExceptionContinuation(x: (Throwable) -> Unit): Continuation<Any?> = ob
|
||||
|
||||
if (!exitCodeMismatch && !goldValueMismatch && this.expectedFail) println("Unexpected pass")
|
||||
}
|
||||
|
||||
List<String> executionCommandLine(String exe) {
|
||||
|
||||
def absoluteTargetToolchain = platformManager.platform(target).absoluteTargetToolchain
|
||||
def absoluteTargetSysRoot = platformManager.platform(target).absoluteTargetSysRoot
|
||||
if (target instanceof KonanTarget.WASM32) {
|
||||
def d8 = "$absoluteTargetToolchain/bin/d8"
|
||||
def launcherJs = "${exe}.js"
|
||||
return [d8, '--expose-wasm', launcherJs, '--', exe]
|
||||
} else if (target instanceof KonanTarget.LINUX_MIPS32 || target instanceof KonanTarget.LINUX_MIPSEL32) {
|
||||
def qemu = target instanceof KonanTarget.LINUX_MIPS32 ? "qemu-mips" : "qemu-mipsel"
|
||||
def absoluteQemu = "$absoluteTargetToolchain/bin/$qemu"
|
||||
return [absoluteQemu, "-L", absoluteTargetSysRoot, exe]
|
||||
} else {
|
||||
return [exe]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TestFailedException extends RuntimeException {
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2010-2018 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.process.ExecResult
|
||||
import org.gradle.process.ExecSpec
|
||||
import org.gradle.util.ConfigureUtil
|
||||
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.konan.target.PlatformManager
|
||||
import org.jetbrains.kotlin.konan.target.Xcode
|
||||
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import java.time.LocalDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/**
|
||||
* A replacement of the standard exec {}
|
||||
* @see org.gradle.api.Project.exec
|
||||
*/
|
||||
interface ExecutorService {
|
||||
fun execute(closure: Closure<in ExecSpec>): ExecResult? = execute(ConfigureUtil.configureUsing(closure))
|
||||
fun execute(action: Action<in ExecSpec>): ExecResult?
|
||||
}
|
||||
|
||||
fun create(project: Project): ExecutorService {
|
||||
val platformManager = project.rootProject.findProperty("platformManager") as PlatformManager
|
||||
val testTarget = platformManager.targetManager(project.findProperty("testTarget") as String?).target
|
||||
val platform = platformManager.platform(testTarget)
|
||||
val absoluteTargetToolchain = platform.absoluteTargetToolchain
|
||||
val absoluteTargetSysRoot = platform.absoluteTargetSysRoot
|
||||
|
||||
return when (testTarget) {
|
||||
KonanTarget.WASM32 -> object : ExecutorService {
|
||||
override fun execute(action: Action<in ExecSpec>): ExecResult? = project.exec { execSpec ->
|
||||
action.execute(execSpec)
|
||||
with(execSpec) {
|
||||
val exe = executable
|
||||
val d8 = "$absoluteTargetToolchain/bin/d8"
|
||||
val launcherJs = "$executable.js"
|
||||
executable = d8
|
||||
args = listOf("--expose-wasm", launcherJs, "--", exe) + args
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KonanTarget.LINUX_MIPS32, KonanTarget.LINUX_MIPSEL32 -> object : ExecutorService {
|
||||
override fun execute(action: Action<in ExecSpec>): ExecResult? = project.exec { execSpec ->
|
||||
action.execute(execSpec)
|
||||
with(execSpec) {
|
||||
val qemu = if (platform.target === KonanTarget.LINUX_MIPS32) "qemu-mips" else "qemu-mipsel"
|
||||
val absoluteQemu = "$absoluteTargetToolchain/bin/$qemu"
|
||||
val exe = executable
|
||||
executable = absoluteQemu
|
||||
args = listOf("-L", absoluteTargetSysRoot, exe) + args
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KonanTarget.IOS_X64 -> simulator(project)
|
||||
|
||||
else -> {
|
||||
if (project.hasProperty("remote")) sshExecutor(project)
|
||||
else object : ExecutorService {
|
||||
override fun execute(action: Action<in ExecSpec>): ExecResult? = project.exec(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a given action with iPhone Simulator.
|
||||
*
|
||||
* The test target should be specified with -Ptest_target=ios_x64
|
||||
* @see KonanTarget.IOS_X64
|
||||
* @param iosDevice an optional project property used to control simulator's device type
|
||||
* Specify -PiosDevice=iPhone X to set it
|
||||
*/
|
||||
private fun simulator(project: Project) : ExecutorService = object : ExecutorService {
|
||||
|
||||
private val simctl by lazy {
|
||||
val sdk = Xcode.current.iphonesimulatorSdk
|
||||
val out = ByteArrayOutputStream()
|
||||
val result = project.exec {
|
||||
it.commandLine("/usr/bin/xcrun", "--find", "simctl", "--sdk", sdk)
|
||||
it.standardOutput = out
|
||||
}
|
||||
result.assertNormalExitValue()
|
||||
out.toString("UTF-8").trim()
|
||||
}
|
||||
|
||||
private val iosDevice = project.findProperty("iosDevice")?.toString() ?: "iPhone 8"
|
||||
|
||||
override fun execute(action: Action<in ExecSpec>): ExecResult? = project.exec { execSpec ->
|
||||
action.execute(execSpec)
|
||||
with(execSpec) { commandLine = listOf(simctl, "spawn", iosDevice, executable) + args }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remote process executor.
|
||||
*
|
||||
* @param remote makes binaries be executed on a remote host
|
||||
* Specify it as -Premote=user@host
|
||||
*/
|
||||
private fun sshExecutor(project: Project) : ExecutorService = object : ExecutorService {
|
||||
|
||||
private val remote: String = project.property("remote").toString()
|
||||
|
||||
// Unique remote dir name to be used in the target host
|
||||
private val remoteDir = run {
|
||||
val date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
|
||||
Paths.get(project.findProperty("remoteRoot").toString(), "tmp",
|
||||
System.getProperty("user.name") + "_" + date).toString()
|
||||
}
|
||||
|
||||
override fun execute(action: Action<in ExecSpec>): ExecResult {
|
||||
var execFile: String? = null
|
||||
|
||||
createRemoteDir()
|
||||
val execResult = project.exec { execSpec ->
|
||||
action.execute(execSpec)
|
||||
with(execSpec) {
|
||||
upload(executable)
|
||||
executable = "$remoteDir/${File(executable).name}"
|
||||
execFile = executable
|
||||
commandLine = arrayListOf("/usr/bin/ssh", remote) + commandLine
|
||||
}
|
||||
}
|
||||
cleanup(execFile!!)
|
||||
return execResult
|
||||
}
|
||||
|
||||
private fun createRemoteDir() {
|
||||
project.exec {
|
||||
it.commandLine("ssh", remote, "mkdir", "-p", remoteDir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun upload(fileName: String) {
|
||||
project.exec {
|
||||
it.commandLine("scp", fileName, "$remote:$remoteDir")
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanup(fileName: String) {
|
||||
project.exec {
|
||||
it.commandLine("ssh", remote, "rm", fileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user