migrated AppleFrameworkIT to junit5 and gradle TestKit

#KT-51553 In Progress

Merge-request: KT-MR-9856
Merged-by: Dmitrii Krasnov <Dmitrii.Krasnov@jetbrains.com>
This commit is contained in:
Dmitrii Krasnov
2023-05-09 07:17:14 +00:00
committed by Space Team
parent b6030b3517
commit 375ff5ed2c
6 changed files with 483 additions and 281 deletions
@@ -18,6 +18,7 @@ import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@GradleTestVersions
@OsCondition
@AndroidTestVersions
@ParameterizedTest(name = "AGP {1} with {0}: {displayName}")
@ArgumentsSource(GradleAndAgpArgumentsProvider.class)
@@ -18,6 +18,7 @@ import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@GradleTestVersions
@OsCondition
@JdkVersions
@ParameterizedTest(name = "{1} with {0}: {displayName}")
@ArgumentsSource(GradleAndJdkArgumentsProvider.class)
@@ -1,234 +1,336 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 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.native
import org.gradle.api.logging.LogLevel
import org.gradle.api.JavaVersion
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.*
import org.jetbrains.kotlin.gradle.testbase.TestVersions
import org.jetbrains.kotlin.gradle.util.AGPVersion
import org.jetbrains.kotlin.gradle.util.modify
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.test.util.KtTestUtil
import org.junit.Assume
import org.junit.BeforeClass
import kotlin.test.Test
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.condition.OS
import kotlin.io.path.absolutePathString
import kotlin.io.path.appendText
class AppleFrameworkIT : BaseGradleIT() {
companion object {
@BeforeClass
@JvmStatic
fun assumeItsMac() {
Assume.assumeTrue(HostManager.hostIsMac)
}
}
@OsCondition(supportedOn = [OS.MAC], enabledOnCI = [OS.MAC])
@DisplayName("Tests for K/N with Apple Framework")
@AndroidTestVersions(minVersion = TestVersions.AGP.AGP_70)
@NativeGradlePluginTests
class AppleFrameworkIT : KGPBaseTest() {
override val defaultGradleVersion = GradleVersionRequired.FOR_MPP_SUPPORT
override fun defaultBuildOptions() = super.defaultBuildOptions().copy(
androidHome = KtTestUtil.findAndroidSdk(),
androidGradlePluginVersion = AGPVersion.v7_4_0,
javaHome = KtTestUtil.getJdk17Home()
)
// TODO(Dmitrii Krasnov): remove it, when KT-58104 will be fixed
override val defaultBuildOptions = super.defaultBuildOptions.copy(statisticsForceValidation = false)
@Test
fun `assemble AppleFrameworkForXcode tasks for IosArm64`() {
with(Project("sharedAppleFramework")) {
val options: BuildOptions = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
@DisplayName("Assembling AppleFrameworkForXcode tasks for IosArm64")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0)
fun shouldAssembleAppleFrameworkForXcodeForIosArm64(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildJdk = jdkProvider.location,
buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
),
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
),
)
)
build("assembleDebugAppleFrameworkForXcodeIosArm64", options = options) {
assertSuccessful()
build(
"assembleDebugAppleFrameworkForXcodeIosArm64",
environmentVariables = environmentVariables,
) {
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeIosArm64")
assertFileExists("/shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework")
assertFileExists("/shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework.dSYM")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework.dSYM")
}
build("assembleCustomDebugAppleFrameworkForXcodeIosArm64", options = options) {
assertSuccessful()
build(
"assembleCustomDebugAppleFrameworkForXcodeIosArm64",
environmentVariables = environmentVariables
) {
assertTasksExecuted(":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64")
assertFileExists("/shared/build/xcode-frameworks/debug/iphoneos123/lib.framework")
assertFileExists("/shared/build/xcode-frameworks/debug/iphoneos123/lib.framework.dSYM")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/lib.framework")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/lib.framework.dSYM")
}
}
}
@Test
fun `assemble fat AppleFrameworkForXcode tasks for Arm64 and X64 simulators`() {
with(Project("sharedAppleFramework")) {
val options: BuildOptions = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "Release",
"SDK_NAME" to "iphonesimulator",
"ARCHS" to "arm64 x86_64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
),
@DisplayName("Assembling fat AppleFrameworkForXcode tasks for Arm64 and X64 simulators")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0)
fun shouldAssembleAppleFrameworkForXcodeForArm64AndX64Simulators(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildJdk = jdkProvider.location,
buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
build("assembleReleaseAppleFrameworkForXcode", options = options) {
assertSuccessful()
) {
val environmentVariables = mapOf(
"CONFIGURATION" to "Release",
"SDK_NAME" to "iphonesimulator",
"ARCHS" to "arm64 x86_64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
build("assembleReleaseAppleFrameworkForXcode", environmentVariables = EnvironmentalVariables(environmentVariables)) {
assertTasksExecuted(":shared:linkReleaseFrameworkIosSimulatorArm64")
assertTasksExecuted(":shared:linkReleaseFrameworkIosX64")
assertTasksExecuted(":shared:assembleReleaseAppleFrameworkForXcode")
assertFileExists("/shared/build/xcode-frameworks/Release/iphonesimulator/sdk.framework")
assertFileExists("/shared/build/xcode-frameworks/Release/iphonesimulator/sdk.framework.dSYM")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/Release/iphonesimulator/sdk.framework")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/Release/iphonesimulator/sdk.framework.dSYM")
}
}
}
@Test
fun `check that macOS framework has symlinks`() {
with(Project("sharedAppleFramework")) {
val options: BuildOptions = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "macosx",
"ARCHS" to "x86_64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to workingDir.absolutePath,
"FRAMEWORKS_FOLDER_PATH" to "${projectName}/build/xcode-derived"
)
@DisplayName("MacOS framework has symlinks")
@OptIn(EnvironmentalVariablesOverride::class)
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
@GradleAndroidTest
fun shouldCheckThatMacOSFrameworkHasSymlinks(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildJdk = jdkProvider.location,
buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
build(":shared:embedAndSignAppleFrameworkForXcode", options = options) {
assertSuccessful()
) {
val environmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "macosx",
"ARCHS" to "x86_64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to projectPath.absolutePathString(),
"FRAMEWORKS_FOLDER_PATH" to "build/xcode-derived"
)
build(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = EnvironmentalVariables(environmentVariables)) {
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeMacosX64")
// Verify symlinks in gradle build folder
assertFileExists("/shared/build/xcode-frameworks/debug/macosx/sdk.framework/Headers")
assertFileIsSymlink("/shared/build/xcode-frameworks/debug/macosx/sdk.framework/Headers")
// Verify symlinks in xcode derived folder
assertFileExists("/build/xcode-derived/sdk.framework/Headers")
assertFileIsSymlink("/build/xcode-derived/sdk.framework/Headers")
assertSymlinkInProjectExists("shared/build/xcode-frameworks/debug/macosx/sdk.framework/Headers")
assertSymlinkInProjectExists("build/xcode-derived/sdk.framework/Headers")
}
}
}
@Test
fun `check embedAndSignAppleFrameworkForXcode fail`() {
with(Project("sharedAppleFramework")) {
build(":shared:embedAndSignAppleFrameworkForXcode") {
assertTasksFailed(":shared:embedAndSignAppleFrameworkForXcode")
assertContains("Please run the embedAndSignAppleFrameworkForXcode task from Xcode")
@DisplayName("embedAndSignAppleFrameworkForXcode fail")
@GradleAndroidTest
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
fun shouldFailWithExecutingEmbedAndSignAppleFrameworkForXcode(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
nativeProject("sharedAppleFramework", gradleVersion, buildJdk = jdkProvider.location) {
buildAndFail(
":shared:embedAndSignAppleFrameworkForXcode",
buildOptions = defaultBuildOptions.copy(androidVersion = agpVersion)
) {
assertOutputContains("Please run the embedAndSignAppleFrameworkForXcode task from Xcode")
}
}
}
@Test
fun `check all registered tasks with Xcode environment for Debug IosArm64 configuration`() {
with(Project("sharedAppleFramework")) {
val currentGradleVersion = chooseWrapperVersionOrFinishTest()
val options: BuildOptions = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "Debug",
"SDK_NAME" to "iphoneos",
"ARCHS" to "arm64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to "testBuildDir",
"FRAMEWORKS_FOLDER_PATH" to "testFrameworksDir"
)
).suppressDeprecationWarningsOn(
"AGP uses deprecated IncrementalTaskInputs (Gradle 7.5)"
) { options ->
GradleVersion.version(currentGradleVersion) >= GradleVersion.version(TestVersions.Gradle.G_7_5) && options.safeAndroidGradlePluginVersion < AGPVersion.v7_3_0
}
build("tasks", options = options) {
assertSuccessful()
assertTasksRegistered(
":shared:assembleDebugAppleFrameworkForXcodeIosArm64",
":shared:embedAndSignAppleFrameworkForXcode",
":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64",
":shared:embedAndSignCustomAppleFrameworkForXcode",
":shared:assembleDebugAppleFrameworkForXcodeIosX64",
":shared:assembleReleaseAppleFrameworkForXcodeIosX64",
":shared:assembleReleaseAppleFrameworkForXcodeIosArm64",
":shared:assembleCustomDebugAppleFrameworkForXcodeIosX64",
":shared:assembleCustomReleaseAppleFrameworkForXcodeIosX64",
":shared:assembleCustomReleaseAppleFrameworkForXcodeIosArm64"
)
}
@DisplayName("Registered tasks with Xcode environment for Debug IosArm64 configuration")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0)
fun shouldCheckAllRegisteredTasksWithXcodeEnvironmentForDebugIosArm64(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
val buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildJdk = jdkProvider.location,
buildOptions = buildOptions
) {
val environmentVariables = mapOf(
"CONFIGURATION" to "Debug",
"SDK_NAME" to "iphoneos",
"ARCHS" to "arm64",
"EXPANDED_CODE_SIGN_IDENTITY" to "-",
"TARGET_BUILD_DIR" to "testBuildDir",
"FRAMEWORKS_FOLDER_PATH" to "testFrameworksDir"
)
buildAndAssertAllTasks(
registeredTasks = listOf(
"shared:assembleDebugAppleFrameworkForXcodeIosArm64",
"shared:embedAndSignAppleFrameworkForXcode",
"shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64",
"shared:embedAndSignCustomAppleFrameworkForXcode",
"shared:assembleDebugAppleFrameworkForXcodeIosX64",
"shared:assembleReleaseAppleFrameworkForXcodeIosX64",
"shared:assembleReleaseAppleFrameworkForXcodeIosArm64",
"shared:assembleCustomDebugAppleFrameworkForXcodeIosX64",
"shared:assembleCustomReleaseAppleFrameworkForXcodeIosX64",
"shared:assembleCustomReleaseAppleFrameworkForXcodeIosArm64"
),
environmentVariables = EnvironmentalVariables(environmentVariables)
)
}
}
@Test
fun `check embedAndSignAppleFrameworkForXcode was registered without required Xcode environments`() {
with(Project("sharedAppleFramework")) {
val currentGradleVersion = chooseWrapperVersionOrFinishTest()
val options: BuildOptions = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
@DisplayName("embedAndSignAppleFrameworkForXcode was registered without required Xcode environments")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
fun shouldCheckEmbedAndSignAppleFrameworkForXcodeDoesNotRequireXcodeEnv(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
val buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildJdk = jdkProvider.location,
buildOptions = buildOptions
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "Debug",
"SDK_NAME" to "iphoneos",
"ARCHS" to "arm64"
)
)
build("tasks", options = options.suppressDeprecationWarningsOn("AGP uses deprecated IncrementalTaskInputs (Gradle 7.5)") {
GradleVersion.version(currentGradleVersion) >= GradleVersion.version(TestVersions.Gradle.G_7_5) && options.safeAndroidGradlePluginVersion < AGPVersion.v7_3_0
}) {
assertSuccessful()
assertTasksRegistered(
":shared:embedAndSignAppleFrameworkForXcode",
":shared:embedAndSignCustomAppleFrameworkForXcode"
)
assertTasksNotRegistered(
":shared:assembleReleaseAppleFrameworkForXcodeIosX64",
":shared:assembleDebugAppleFrameworkForXcodeIosX64",
":shared:assembleReleaseAppleFrameworkForXcodeIosArm64",
":shared:assembleDebugAppleFrameworkForXcodeIosArm64",
":shared:assembleCustomDebugAppleFrameworkForXcodeIosX64",
":shared:assembleCustomReleaseAppleFrameworkForXcodeIosX64",
":shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64",
":shared:assembleCustomReleaseAppleFrameworkForXcodeIosArm64"
)
}
build(":shared:embedAndSignCustomAppleFrameworkForXcode", options = options) {
buildAndAssertAllTasks(
registeredTasks = listOf(
"shared:embedAndSignAppleFrameworkForXcode",
"shared:embedAndSignCustomAppleFrameworkForXcode"
),
notRegisteredTasks = listOf(
"shared:assembleReleaseAppleFrameworkForXcodeIosX64",
"shared:assembleDebugAppleFrameworkForXcodeIosX64",
"shared:assembleReleaseAppleFrameworkForXcodeIosArm64",
"shared:assembleDebugAppleFrameworkForXcodeIosArm64",
"shared:assembleCustomDebugAppleFrameworkForXcodeIosX64",
"shared:assembleCustomReleaseAppleFrameworkForXcodeIosX64",
"shared:assembleCustomDebugAppleFrameworkForXcodeIosArm64",
"shared:assembleCustomReleaseAppleFrameworkForXcodeIosArm64"
),
environmentVariables = environmentVariables
)
buildAndFail(
":shared:embedAndSignCustomAppleFrameworkForXcode",
environmentVariables = environmentVariables
) {
assertTasksFailed(":shared:embedAndSignCustomAppleFrameworkForXcode")
assertContains("Please run the embedAndSignCustomAppleFrameworkForXcode task from Xcode")
assertOutputContains("Please run the embedAndSignCustomAppleFrameworkForXcode task from Xcode")
}
}
}
@Test
fun `check that static framework for Arm64 is built but is not embedded`() {
with(Project("sharedAppleFramework")) {
val options: BuildOptions = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
@DisplayName("Static framework for Arm64 is built but is not embedded")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_0)
fun shouldCheckThatStaticFrameworkForArm64IsBuildAndNotEmbedded(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildJdk = jdkProvider.location,
buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
),
)
)
setupWorkingDir()
projectDir.resolve("shared/build.gradle.kts").modify {
subProject("shared").buildGradleKts.modify {
it.replace(
"baseName = \"sdk\"",
"baseName = \"sdk\"\nisStatic = true"
)
}
build("embedAndSignAppleFrameworkForXcode", options = options) {
assertSuccessful()
build("embedAndSignAppleFrameworkForXcode", environmentVariables = environmentVariables) {
assertTasksExecuted(":shared:assembleDebugAppleFrameworkForXcodeIosArm64")
assertTasksNotExecuted(":shared:embedAndSignAppleFrameworkForXcode")
assertFileExists("/shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework")
assertNoSuchFile("/shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework.dSYM")
assertTasksSkipped(":shared:embedAndSignAppleFrameworkForXcode")
assertDirectoryInProjectExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework")
assertFileInProjectNotExists("shared/build/xcode-frameworks/debug/iphoneos123/sdk.framework.dSYM")
}
}
}
@DisplayName("Configuration errors reported to Xcode when embedAndSign task requested")
@OptIn(EnvironmentalVariablesOverride::class)
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
@GradleAndroidTest
fun shouldReportConfErrorsToXcodeWhenRequestedByEmbedAndSign(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
val buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildJdk = jdkProvider.location,
buildOptions = buildOptions
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
@Test
fun `configuration errors reported to Xcode when embedAndSign task requested`() {
with(Project("sharedAppleFramework")) {
setupWorkingDir()
projectDir.resolve("shared/build.gradle.kts").appendText(
subProject("shared").buildGradleKts.appendText(
"""
kotlin {
sourceSets["commonMain"].dependencies {
@@ -238,174 +340,227 @@ class AppleFrameworkIT : BaseGradleIT() {
""".trimIndent()
)
build(
":shared:embedAndSignAppleFrameworkForXcode",
options = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
) {
assertFailed()
assertContains("error: Could not find com.example.unknown:dependency:0.0.1.")
buildAndFail(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = environmentVariables) {
assertOutputContains("error: Could not find com.example.unknown:dependency:0.0.1.")
}
}
}
@Test
fun `compilation errors reported to Xcode when embedAndSign task requested`() {
with(Project("sharedAppleFramework")) {
setupWorkingDir()
projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
@DisplayName("Compilation errors reported to Xcode when embedAndSign task requested")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
fun shouldReportCompilationErrorsToXcodeWhenRequestedByEmbedAndSign(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
val buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildOptions = buildOptions,
buildJdk = jdkProvider.location
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
projectPath.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
.appendText("this can't be compiled")
build(
":shared:embedAndSignAppleFrameworkForXcode",
options = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
) {
assertFailed()
assertContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration")
assertContains("error: Compilation finished with errors")
buildAndFail(":shared:embedAndSignAppleFrameworkForXcode", environmentVariables = environmentVariables) {
assertOutputContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration")
assertOutputContains("error: Compilation finished with errors")
}
}
}
@DisplayName("Compilation errors printed with Gradle-style when any other task requested")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
fun shouldPrintCompilationErrorsWithGradleStyle(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
val buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildOptions = buildOptions,
buildJdk = jdkProvider.location
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
@Test
fun `compilation errors printed with Gradle-style when any other task requested`() {
with(Project("sharedAppleFramework")) {
setupWorkingDir()
projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
projectPath.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
.appendText("this can't be compiled")
build(
buildAndFail(":shared:assembleDebugAppleFrameworkForXcodeIosArm64", environmentVariables = environmentVariables) {
assertOutputContains("e: file:///")
assertOutputDoesNotContain("error: Compilation finished with errors")
}
}
}
@DisplayName("Compilation errors printed with Xcode-style with explicit option")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
fun shouldPrintCompilationErrorsWithXcodeStyle(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
val buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildOptions = buildOptions,
buildJdk = jdkProvider.location
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
projectPath.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
.appendText("this can't be compiled")
buildAndFail(
":shared:assembleDebugAppleFrameworkForXcodeIosArm64",
options = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
"-Pkotlin.native.useXcodeMessageStyle=true",
environmentVariables = environmentVariables
) {
assertFailed()
assertContains("e: file:///")
assertNotContains("error: Compilation finished with errors")
assertOutputContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration")
assertOutputContains("error: Compilation finished with errors")
}
}
}
@DisplayName("Compilation errors reported to Xcode when embedAndSign task requested and compiler runs in a separate process")
@OptIn(EnvironmentalVariablesOverride::class)
@GradleAndroidTest
// We need to use Gradle 7.2 here because of the issue https://github.com/gradle/gradle/issues/13957
@GradleTestVersions(minVersion = TestVersions.Gradle.G_7_2)
fun shouldReportErrorsToXcodeWhenEmbedAndSignRequestedAndDisableCompilerDaemon(
gradleVersion: GradleVersion,
agpVersion: String,
jdkProvider: JdkVersions.ProvidedJdk
) {
val buildOptions = defaultBuildOptions.copy(
androidVersion = agpVersion
)
nativeProject(
"sharedAppleFramework",
gradleVersion,
buildOptions = buildOptions,
buildJdk = jdkProvider.location
) {
val environmentVariables = EnvironmentalVariables(
mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
@Test
fun `compilation errors printed with Xcode-style with explicit option`() {
with(Project("sharedAppleFramework")) {
setupWorkingDir()
projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
projectPath.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
.appendText("this can't be compiled")
build(
":shared:assembleDebugAppleFrameworkForXcodeIosArm64", "-Pkotlin.native.useXcodeMessageStyle=true",
options = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
buildAndFail(
":shared:embedAndSignAppleFrameworkForXcode",
"-Pkotlin.native.disableCompilerDaemon=true",
environmentVariables = environmentVariables
) {
assertFailed()
assertContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration")
assertContains("error: Compilation finished with errors")
assertOutputContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration")
}
}
}
@Test
fun `compilation errors reported to Xcode when embedAndSign task requested and compiler runs in a separate process`() {
with(Project("sharedAppleFramework")) {
setupWorkingDir()
projectDir.resolve("shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt")
.appendText("this can't be compiled")
build(
":shared:embedAndSignAppleFrameworkForXcode", "-Pkotlin.native.disableCompilerDaemon=true",
options = defaultBuildOptions().copy(
customEnvironmentVariables = mapOf(
"CONFIGURATION" to "debug",
"SDK_NAME" to "iphoneos123",
"ARCHS" to "arm64",
"TARGET_BUILD_DIR" to "no use",
"FRAMEWORKS_FOLDER_PATH" to "no use"
)
)
) {
assertFailed()
assertContains("/sharedAppleFramework/shared/src/commonMain/kotlin/com/github/jetbrains/myapplication/Greeting.kt:7:2: error: Expecting a top level declaration")
}
}
}
@Test
fun `frameworks can be consumed from other gradle project`() {
with(Project("consumableAppleFrameworks", minLogLevel = LogLevel.INFO)) {
setupWorkingDir()
@DisplayName("Frameworks can be consumed from other gradle project")
@GradleTest
fun shouldCheckFrameworksCanBeConsumedFromOtherGradleProjects(gradleVersion: GradleVersion) {
nativeProject("consumableAppleFrameworks", gradleVersion) {
build(":consumer:help") {
assertContains("RESOLUTION_SUCCESS")
assertNotContains("RESOLUTION_FAILURE")
assertOutputContains("RESOLUTION_SUCCESS")
assertOutputDoesNotContain("RESOLUTION_FAILURE")
}
}
}
@Test
fun `smoke test with apple gradle plugin`() {
with(Project("appleGradlePluginConsumesAppleFrameworks", minLogLevel = LogLevel.INFO)) {
setupWorkingDir()
@DisplayName("Smoke test with apple gradle plugin")
@GradleWithJdkTest
@JdkVersions(versions = [JavaVersion.VERSION_11])
fun smokeTestWithAppleGradlePlugin(
gradleVersion: GradleVersion,
providedJdk: JdkVersions.ProvidedJdk
) {
nativeProject(
"appleGradlePluginConsumesAppleFrameworks",
gradleVersion,
buildJdk = providedJdk.location
) {
fun dependencyInsight(configuration: String) = arrayOf(
":iosApp:dependencyInsight", "--configuration", configuration, "--dependency", "iosLib"
)
fun CompiledProject.assertContainsVariant(variantName: String) {
assertContains("Variant $variantName")
fun BuildResult.assertContainsVariant(variantName: String) {
assertOutputContains(
if (gradleVersion >= GradleVersion.version(TestVersions.Gradle.G_7_5))
"Variant $variantName"
else "variant \"$variantName\""
)
}
build(*dependencyInsight("iosAppIosX64DebugImplementation")) {
assertSuccessful()
assertContainsVariant("mainDynamicDebugFrameworkIos")
}
build(*dependencyInsight("iosAppIosX64ReleaseImplementation")) {
assertSuccessful()
assertContainsVariant("mainDynamicReleaseFrameworkIos")
}
// NB: '0' is required at the end since dependency is added with custom attribute and it creates new configuration
build(*dependencyInsight("iosAppIosX64DebugImplementation0"), "-PmultipleFrameworks") {
assertSuccessful()
assertContainsVariant("mainStaticDebugFrameworkIos")
}
build(*dependencyInsight("iosAppIosX64ReleaseImplementation0"), "-PmultipleFrameworks") {
assertSuccessful()
assertContainsVariant("mainStaticReleaseFrameworkIos")
}
}
}
}
@@ -97,6 +97,30 @@ fun GradleProject.assertFileNotExistsInTree(
assertFileNotExistsInTree(projectPath.resolve(pathToTreeRoot), fileName)
}
/**
* Asserts symlink under [path] exists and is a symlink
*/
fun assertSymlinkExists(
path: Path
) {
assert(Files.exists(path)) {
"Symlink '${path}' does not exist!"
}
assert(Files.isSymbolicLink(path)) {
"'${path}' is not a symlink!"
}
}
/**
* Asserts symlink under [pathToFile] relative to the test project exists and is a symlink.
*/
fun TestProject.assertSymlinkInProjectExists(
pathToFile: String
) {
assertSymlinkExists(projectPath.resolve(pathToFile))
}
/**
* Asserts directory under [pathToDir] relative to the test project exists and is a directory.
*/
@@ -105,9 +129,9 @@ fun GradleProject.assertDirectoryInProjectExists(
) = assertDirectoryExists(projectPath.resolve(pathToDir))
/**
* Asserts directory under [file] exists and is a directory.
* Asserts directory under [dirPath] exists and is a directory.
*/
fun GradleProject.assertDirectoryExists(
fun assertDirectoryExists(
dirPath: Path
) = assertDirectoriesExist(dirPath)
@@ -132,7 +156,7 @@ fun assertDirectoriesExist(
}
/**
* Asserts file under [pathToFile] relative to the test project exists and contains all the lines from [expectedLines]
* Asserts file under [pathToFile] relative to the test project exists and contains all the lines from [expectedText]
*/
fun GradleProject.assertFileInProjectContains(
pathToFile: String,
@@ -160,14 +160,17 @@ fun BuildResult.assertNativeTasksClasspath(
* it could contain task paths as well, but without the first semicolon.
* @param notRegisteredTasks An optional list of task names that should not have been registered,
* it could contain task paths as well, but without the first semicolon.
* @param environmentVariables environmental variables for build process
* @throws AssertionError if any of the registered tasks do not match the expected task names,
* or if any of the not-registered tasks were actually registered.
*/
@OptIn(EnvironmentalVariablesOverride::class)
fun TestProject.buildAndAssertAllTasks(
registeredTasks: List<String> = emptyList(),
notRegisteredTasks: List<String> = emptyList()
notRegisteredTasks: List<String> = emptyList(),
environmentVariables: EnvironmentalVariables = EnvironmentalVariables()
) {
build("tasks", "--all") {
build("tasks", "--all", environmentVariables = environmentVariables) {
assertTasksInBuildOutput(registeredTasks, notRegisteredTasks)
}
}
@@ -33,6 +33,7 @@ import kotlin.test.assertTrue
* @param [buildOptions] common Gradle build options
* @param [buildJdk] path to JDK build should run with. *Note* Only append to 'gradle.properties'!
*/
@OptIn(EnvironmentalVariablesOverride::class)
fun KGPBaseTest.project(
projectName: String,
gradleVersion: GradleVersion,
@@ -44,7 +45,8 @@ fun KGPBaseTest.project(
projectPathAdditionalSuffix: String = "",
buildJdk: File? = null,
localRepoDir: Path? = null,
test: TestProject.() -> Unit = {},
environmentVariables: EnvironmentalVariables = EnvironmentalVariables(),
test: TestProject.() -> Unit = {}
): TestProject {
val projectPath = setupProjectFromTestResources(
projectName,
@@ -56,7 +58,6 @@ fun KGPBaseTest.project(
projectPath.enableCacheRedirector()
projectPath.enableAndroidSdk()
val gradleRunner = GradleRunner
.create()
.withProjectDir(projectPath.toFile())
@@ -72,6 +73,7 @@ fun KGPBaseTest.project(
forceOutput = forceOutput,
enableBuildScan = enableBuildScan,
enableGradleDebug = enableGradleDebug,
environmentVariables = environmentVariables
)
addHeapDumpOptions.ifTrue { testProject.addHeapDumpOptions() }
localRepoDir?.let { testProject.configureLocalRepository(localRepoDir) }
@@ -93,6 +95,7 @@ fun KGPBaseTest.project(
* @param [buildOptions] common Gradle build options
* @param [buildJdk] path to JDK build should run with. *Note* Only append to 'gradle.properties'!
*/
@OptIn(EnvironmentalVariablesOverride::class)
fun KGPBaseTest.nativeProject(
projectName: String,
gradleVersion: GradleVersion,
@@ -104,7 +107,8 @@ fun KGPBaseTest.nativeProject(
projectPathAdditionalSuffix: String = "",
buildJdk: File? = null,
localRepoDir: Path? = null,
test: TestProject.() -> Unit = {},
environmentVariables: EnvironmentalVariables = EnvironmentalVariables(),
test: TestProject.() -> Unit = {}
): TestProject {
val project = project(
projectName = projectName,
@@ -117,6 +121,7 @@ fun KGPBaseTest.nativeProject(
projectPathAdditionalSuffix = projectPathAdditionalSuffix,
buildJdk = buildJdk,
localRepoDir = localRepoDir,
environmentVariables = environmentVariables,
)
project.configureSingleNativeTarget()
project.test()
@@ -126,6 +131,7 @@ fun KGPBaseTest.nativeProject(
/**
* Trigger test project build with given [buildArguments] and assert build is successful.
*/
@OptIn(EnvironmentalVariablesOverride::class)
fun TestProject.build(
vararg buildArguments: String,
forceOutput: Boolean = this.forceOutput,
@@ -134,7 +140,8 @@ fun TestProject.build(
enableBuildCacheDebug: Boolean = false,
enableBuildScan: Boolean = this.enableBuildScan,
buildOptions: BuildOptions = this.buildOptions,
assertions: BuildResult.() -> Unit = {},
environmentVariables: EnvironmentalVariables = this.environmentVariables,
assertions: BuildResult.() -> Unit = {}
) {
if (enableBuildScan) agreeToBuildScanService()
@@ -148,6 +155,7 @@ fun TestProject.build(
)
val gradleRunnerForBuild = gradleRunner
.also { if (forceOutput) it.forwardOutput() }
.also { if (environmentVariables.environmentalVariables.isNotEmpty()) it.withEnvironment(System.getenv() + environmentVariables.environmentalVariables) }
.withDebug(enableGradleDebug)
.withArguments(allBuildArguments)
withBuildSummary(allBuildArguments) {
@@ -169,7 +177,8 @@ fun TestProject.buildAndFail(
enableBuildCacheDebug: Boolean = false,
enableBuildScan: Boolean = this.enableBuildScan,
buildOptions: BuildOptions = this.buildOptions,
assertions: BuildResult.() -> Unit = {},
environmentVariables: EnvironmentalVariables = this.environmentVariables,
assertions: BuildResult.() -> Unit = {}
) {
if (enableBuildScan) agreeToBuildScanService()
@@ -183,6 +192,7 @@ fun TestProject.buildAndFail(
)
val gradleRunnerForBuild = gradleRunner
.also { if (forceOutput) it.forwardOutput() }
.also { if (environmentVariables.environmentalVariables.isNotEmpty()) it.withEnvironment(System.getenv() + environmentVariables.environmentalVariables) }
.withDebug(enableGradleDebug)
.withArguments(allBuildArguments)
withBuildSummary(allBuildArguments) {
@@ -295,6 +305,13 @@ open class GradleProject(
): List<Path> = files.map { projectPath.relativize(it) }
}
@JvmInline
value class EnvironmentalVariables @EnvironmentalVariablesOverride constructor(val environmentalVariables: Map<String, String> = emptyMap())
@RequiresOptIn("Environmental variables override may lead to interference of parallel builds and breaks Gradle tests debugging")
annotation class EnvironmentalVariablesOverride
@OptIn(EnvironmentalVariablesOverride::class)
class TestProject(
val gradleRunner: GradleRunner,
projectName: String,
@@ -313,6 +330,7 @@ class TestProject(
* Note that we'll need to let the debugger start listening at this port first *before* the Kotlin daemon is launched.
*/
val kotlinDaemonDebugPort: Int? = null,
val environmentVariables: EnvironmentalVariables = EnvironmentalVariables(),
) : GradleProject(projectName, projectPath) {
fun subProject(name: String) = GradleProject(name, projectPath.resolve(name))