Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

package org.jetbrains.kotlin.gradle.android.externalAndroidTarget

import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.kotlin.dsl.kotlin
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import java.util.zip.ZipFile
Expand Down Expand Up @@ -380,37 +378,6 @@ class AndroidLibraryWithJavaIT : KGPBaseTest() {
}
}

private fun externalAndroidLibraryProject(
gradleVersion: GradleVersion,
androidVersion: String,
jdkVersion: JdkVersions.ProvidedJdk,
namespace: String,
withJava: Boolean = false,
androidLibraryConfiguration: KotlinMultiplatformAndroidLibraryTarget.() -> Unit = {},
configureProject: TestProject.() -> Unit = {},
): TestProject = project(
"empty",
gradleVersion = gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = androidVersion),
buildJdk = jdkVersion.location,
) {
plugins {
kotlin("multiplatform")
id("com.android.kotlin.multiplatform.library")
}
buildScriptInjection {
kotlinMultiplatform.apply {
targets.withType(KotlinMultiplatformAndroidLibraryTarget::class.java).configureEach { target ->
target.compileSdk = 34
target.namespace = namespace
if (withJava) target.withJava()
target.androidLibraryConfiguration()
}
}
}
configureProject()
}

private fun TestProject.assertAarContainsClass(aarPath: String, classPath: String) {
val aarFile = projectPath.resolve(aarPath).toFile()
check(aarFile.exists()) { "AAR file does not exist: $aarPath" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* Copyright 2010-2026 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.android.externalAndroidTarget

import com.android.build.api.dsl.KotlinMultiplatformAndroidDeviceTestCompilation
import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import kotlin.test.assertEquals
import kotlin.test.assertTrue

// Used AGP 9.0 as the minimal stable version supporting the external Android target plugin (com.android.kotlin.multiplatform.library).
@AndroidTestVersions(minVersion = TestVersions.AGP.AGP_90)
@AndroidGradlePluginTests
class AndroidTestReportsExternalAndroidTargetIT : KGPBaseTest() {

// Uses `com.android.kotlin.multiplatform.library`, requires AGP new DSL.
override val defaultBuildOptions: BuildOptions
get() = super.defaultBuildOptions.copy(enableLegacyAgpDsl = false)

@GradleAndroidTest
fun `test - host tests execute and generate reports`(
gradleVersion: GradleVersion,
androidVersion: String,
jdkVersion: JdkVersions.ProvidedJdk,
) {
externalAndroidLibraryProject(
gradleVersion = gradleVersion,
androidVersion = androidVersion,
jdkVersion = jdkVersion,
namespace = "org.jetbrains.sample.hosttestreports",
androidLibraryConfiguration = {
withHostTest {}
},
) {
buildScriptInjection {
kotlinMultiplatform.apply {
sourceSets.getByName("commonTest").dependencies {
implementation(kotlin("test"))
}
}
}

kotlinSourcesDir("commonTest").resolve("CommonTestBase.kt").apply {
parent.toFile().mkdirs()
toFile().writeText(
"""
package org.jetbrains.sample

import kotlin.test.Test
import kotlin.test.assertTrue

open class CommonTestBase {
@Test
fun testInCommon() {
assertTrue(true)
}
}
""".trimIndent()
)
}

kotlinSourcesDir("androidHostTest").resolve("HostTest.kt").apply {
parent.toFile().mkdirs()
toFile().writeText(
"""
package org.jetbrains.sample

import kotlin.test.Test
import kotlin.test.assertEquals

class HostTest : CommonTestBase() {
@Test
fun testInHost() {
assertEquals("host", "host")
}
}
""".trimIndent()
)
}

build(":testAndroidHostTest") {
assertTasksExecuted(":compileAndroidHostTest")
assertTasksExecuted(":testAndroidHostTest")
assertFileInProjectExists("build/reports/tests/testAndroidHostTest/index.html")
assertExecutedTestCases(
":testAndroidHostTest",
"org.jetbrains.sample.CommonTestBase#testInCommon",
"org.jetbrains.sample.HostTest#testInCommon",
"org.jetbrains.sample.HostTest#testInHost",
)
}
}
}

@GradleAndroidTest
fun `test - device test wiring`(
gradleVersion: GradleVersion,
androidVersion: String,
jdkVersion: JdkVersions.ProvidedJdk,
) {
externalAndroidLibraryProject(
gradleVersion = gradleVersion,
androidVersion = androidVersion,
jdkVersion = jdkVersion,
namespace = "org.jetbrains.sample.devicetestwiring",
androidLibraryConfiguration = {
withHostTest {}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: do we need to configure withHostTest {} in this test case?

withDeviceTest {
instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
},
) {
// `androidDeviceTest` belongs to the `instrumentedTest` source set tree and therefore is not
// connected to `commonTest` by the default hierarchy template: the edge has to be declared.
buildScriptInjection {
kotlinMultiplatform.apply {
sourceSets.getByName("androidDeviceTest").dependsOn(sourceSets.getByName("commonTest"))
}
}

kotlinSourcesDir("commonTest").resolve("CommonTestUtils.kt").apply {
parent.toFile().mkdirs()
toFile().writeText(
"""
package org.jetbrains.sample

object CommonTestUtils {
fun expected(): String = "expected"
}
""".trimIndent()
)
}

kotlinSourcesDir("androidDeviceTest").resolve("DeviceTest.kt").apply {
parent.toFile().mkdirs()
toFile().writeText(
"""
package org.jetbrains.sample

class DeviceTest {
fun value(): String = CommonTestUtils.expected()
}
""".trimIndent()
)
}

val configuration = buildScriptReturn {
val hasConnectedTask = project.tasks.names.contains("connectedAndroidDeviceTest")

val target = kotlinMultiplatform.targets.withType(KotlinMultiplatformAndroidLibraryTarget::class.java).single()
val compilation = target.compilations.getByName("deviceTest") as KotlinMultiplatformAndroidDeviceTestCompilation

hasConnectedTask to compilation.instrumentationRunner
}.buildAndReturn(":help")

assertTrue(configuration.first, "Task connectedAndroidDeviceTest must be registered in task graph")
assertEquals("androidx.test.runner.AndroidJUnitRunner", configuration.second)

// The declared `dependsOn` edge is honoured: device test sources see `commonTest` code.
build(":compileAndroidDeviceTest") {
assertTasksExecuted(":compileAndroidDeviceTest")
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,44 @@

package org.jetbrains.kotlin.gradle.testbase

import com.android.build.api.dsl.KotlinMultiplatformAndroidLibraryTarget
import org.gradle.api.publish.PublishingExtension
import org.gradle.jvm.toolchain.JavaLanguageVersion
import org.gradle.kotlin.dsl.kotlin
import org.gradle.util.GradleVersion
import java.io.File

fun KGPBaseTest.externalAndroidLibraryProject(
gradleVersion: GradleVersion,
androidVersion: String,
jdkVersion: JdkVersions.ProvidedJdk,
namespace: String = "org.jetbrains.sample.androidlibrary",
withJava: Boolean = false,
androidLibraryConfiguration: KotlinMultiplatformAndroidLibraryTarget.() -> Unit = {},
configureProject: TestProject.() -> Unit = {},
): TestProject = project(
"empty",
gradleVersion = gradleVersion,
buildOptions = defaultBuildOptions.copy(androidVersion = androidVersion),
buildJdk = jdkVersion.location,
) {
plugins {
kotlin("multiplatform")
id("com.android.kotlin.multiplatform.library")
}
buildScriptInjection {
kotlinMultiplatform.apply {
targets.withType(KotlinMultiplatformAndroidLibraryTarget::class.java).configureEach { target ->
target.compileSdk = 34

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: let's eventually set this to the latest SDK (37)?

target.namespace = namespace
if (withJava) target.withJava()
target.androidLibraryConfiguration()
}
}
}
configureProject()
}

fun KGPBaseTest.kotlinAndroidLibraryProject(
gradleVersion: GradleVersion,
agpVersion: String,
Expand Down