KT-65582 Extract xcode simulator test helpers

This commit is contained in:
Andrey Yastrebov
2024-02-12 10:59:03 +01:00
committed by Space Team
parent 6ed6e7ce28
commit 444dc790db
3 changed files with 132 additions and 88 deletions
@@ -5,20 +5,12 @@
package org.jetbrains.kotlin.gradle.native
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.jetbrains.kotlin.gradle.util.assertProcessRunResult
import org.jetbrains.kotlin.gradle.util.runProcess
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.condition.OS
import java.io.File
import java.util.UUID
@DisplayName("tests for the K/N XCode simulator test infrastructure")
@NativeGradlePluginTests
@@ -27,18 +19,20 @@ class NativeXcodeSimulatorTestsIT : KGPBaseTest() {
@DisplayName("A user-friendly error message is produced when the standalone mode is disabled and no simulator has booted")
@GradleTest
fun checkNoSimulatorErrorMessage(gradleVersion: GradleVersion) {
val unbootedSimulator = createSimulator()
project("native-test-ios-https-request", gradleVersion) {
buildGradleKts.append(
"""
XCTestHelpers().use {
val unbootedSimulator = it.createSimulator()
project("native-test-ios-https-request", gradleVersion) {
buildGradleKts.append(
"""
tasks.withType<org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest> {
device.set("${unbootedSimulator.udid}")
standalone.set(false)
}
""".trimIndent()
)
buildAndFail("check") {
assertOutputContains("The problem can be that you have not booted the required device or have configured the task to a different simulator. Please check the task output and its device configuration.")
)
buildAndFail("check") {
assertOutputContains("The problem can be that you have not booted the required device or have configured the task to a different simulator. Please check the task output and its device configuration.")
}
}
}
}
@@ -57,82 +51,28 @@ class NativeXcodeSimulatorTestsIT : KGPBaseTest() {
@DisplayName("iOS simulator test with an https request doesn't fail with the standalone mode disabled")
@GradleTest
fun checkSimulatorTestDoesNotFailInNonStandaloneMode(gradleVersion: GradleVersion) {
project("native-test-ios-https-request", gradleVersion) {
val simulator = createSimulator()
simulator.boot()
buildGradleKts.append(
"""
XCTestHelpers().use {
val simulator = it.createSimulator().apply {
boot()
}
project("native-test-ios-https-request", gradleVersion) {
buildGradleKts.append(
"""
tasks.withType<org.jetbrains.kotlin.gradle.targets.native.tasks.KotlinNativeSimulatorTest> {
device.set("${simulator.udid}")
standalone.set(false)
}
""".trimIndent()
)
build("check") {
when (HostManager.host) {
KonanTarget.MACOS_ARM64 -> assertTasksExecuted(":iosSimulatorArm64Test")
KonanTarget.MACOS_X64 -> assertTasksExecuted(":iosX64Test")
else -> error("Unexpected host")
)
build("check") {
when (HostManager.host) {
KonanTarget.MACOS_ARM64 -> assertTasksExecuted(":iosSimulatorArm64Test")
KonanTarget.MACOS_X64 -> assertTasksExecuted(":iosX64Test")
else -> error("Unexpected host")
}
}
}
}
}
@Serializable
private data class Device(val name: String, val udid: String)
@Serializable
private data class Simulators(val devices: Map<String, List<Device>>)
private val deviceIdentifier = "com.apple.CoreSimulator.SimDeviceType.iPhone-12-Pro-Max"
private val uuid = UUID.randomUUID()
private val testSimulatorName = "NativeXcodeSimulatorTestsIT_${uuid}_simulator"
@AfterAll
fun removeSimulatorsCreatedForTests() {
simulators().devices.values.toList().flatMap { it }.filter {
it.name == testSimulatorName
}.forEach {
processOutput(
listOf("/usr/bin/xcrun", "simctl", "delete", it.udid)
)
}
}
private fun simulators(): Simulators {
return Json {
ignoreUnknownKeys = true
}.decodeFromString<Simulators>(
processOutput(
listOf("/usr/bin/xcrun", "simctl", "list", "devices", "-j")
)
)
}
private fun createSimulator(): Device {
return Device(
testSimulatorName,
processOutput(
listOf("/usr/bin/xcrun", "simctl", "create", testSimulatorName, deviceIdentifier)
).dropLast(1)
)
}
private fun Device.boot() {
processOutput(
listOf("/usr/bin/xcrun", "simctl", "bootstatus", udid, "-bd")
)
}
private fun processOutput(arguments: List<String>): String {
val result = runProcess(
arguments, File("."),
redirectErrorStream = false,
)
assertProcessRunResult(
result
) {
assert(isSuccessful)
}
return result.output
}
}
@@ -12,12 +12,19 @@ import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.test.assertEquals
internal enum class XcodeBuildMode {
NORMAL,
TEST
}
fun TestProject.buildXcodeProject(
internal fun TestProject.buildXcodeProject(
xcodeproj: Path,
scheme: String = "iosApp",
configuration: String = "Debug",
destination: String = "generic/platform=iOS Simulator"
destination: String = "generic/platform=iOS Simulator",
sdk: String = "iphonesimulator",
buildMode: XcodeBuildMode = XcodeBuildMode.NORMAL,
extraArguments: Map<String, Any> = emptyMap(),
) {
prepareForXcodebuild()
@@ -25,11 +32,14 @@ fun TestProject.buildXcodeProject(
xcodeproj = xcodeproj,
scheme = scheme,
configuration = configuration,
sdk = sdk,
destination = destination,
buildMode = buildMode,
extraArguments = extraArguments
)
}
fun TestProject.xcodebuild(
internal fun TestProject.xcodebuild(
workingDir: Path = projectPath,
xcodeproj: Path? = null,
workspace: Path? = null,
@@ -38,6 +48,8 @@ fun TestProject.xcodebuild(
sdk: String? = null,
arch: String? = null,
destination: String? = null,
buildMode: XcodeBuildMode = XcodeBuildMode.NORMAL,
extraArguments: Map<String, Any> = emptyMap(),
derivedDataPath: Path? = projectPath.resolve("xcodeDerivedData"),
) {
xcodebuild(
@@ -49,6 +61,12 @@ fun TestProject.xcodebuild(
}
}
infix fun String.eq(value: Any?) {
if (value != null) {
add("${this}=$value")
}
}
add("xcodebuild")
"-project" set xcodeproj
"-workspace" set workspace
@@ -58,12 +76,20 @@ fun TestProject.xcodebuild(
"-arch" set arch
"-destination" set destination
"-derivedDataPath" set derivedDataPath
extraArguments.forEach {
it.key eq it.value
}
if (buildMode == XcodeBuildMode.TEST) {
add("test")
}
},
workingDir,
)
}
fun TestProject.prepareForXcodebuild() {
internal fun TestProject.prepareForXcodebuild() {
overrideMavenLocalIfNeeded()
gradleProperties
@@ -0,0 +1,78 @@
/*
* Copyright 2010-2024 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.gradle.testbase
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import org.jetbrains.kotlin.gradle.util.assertProcessRunResult
import org.jetbrains.kotlin.gradle.util.runProcess
import java.io.Closeable
import java.io.File
import java.util.*
internal class XCTestHelpers : Closeable {
@Serializable
data class Device(val name: String, val udid: String)
@Serializable
data class Simulators(val devices: Map<String, List<Device>>)
private val deviceIdentifier = "com.apple.CoreSimulator.SimDeviceType.iPhone-12-Pro-Max"
private val uuid = UUID.randomUUID()
private val testSimulatorName = "NativeXcodeSimulatorTestsIT_${uuid}_simulator"
fun createSimulator(): Device {
return Device(
testSimulatorName,
processOutput(
listOf("/usr/bin/xcrun", "simctl", "create", testSimulatorName, deviceIdentifier)
).dropLast(1)
)
}
private val json = Json {
ignoreUnknownKeys = true
}
private fun simulators(): Simulators {
return json.decodeFromString<Simulators>(
processOutput(
listOf("/usr/bin/xcrun", "simctl", "list", "devices", "-j")
)
)
}
override fun close() {
simulators().devices.values.toList().flatten().filter {
it.name == testSimulatorName
}.forEach {
processOutput(
listOf("/usr/bin/xcrun", "simctl", "delete", it.udid)
)
}
}
}
internal fun XCTestHelpers.Device.boot() {
processOutput(
listOf("/usr/bin/xcrun", "simctl", "bootstatus", udid, "-bd")
)
}
private fun processOutput(arguments: List<String>): String {
val result = runProcess(
arguments, File("."),
redirectErrorStream = false,
)
assertProcessRunResult(
result
) {
assert(isSuccessful)
}
return result.output
}