diff --git a/build.gradle.kts b/build.gradle.kts index 018e700..7e4018f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,7 @@ plugins { alias(libs.plugins.android.test) apply false alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.multiplatform) apply false alias(libs.plugins.publicationsReport) } diff --git a/demo-project/app/build.gradle.kts b/demo-project/app/build.gradle.kts index 00e31fe..3893e9c 100644 --- a/demo-project/app/build.gradle.kts +++ b/demo-project/app/build.gradle.kts @@ -64,6 +64,6 @@ dependencies { implementation(libs.androidx.navigation.fragment) implementation(libs.androidx.navigation.ui) - testImplementation(libs.junit) + testImplementation(libs.kotlin.test) testImplementation(libs.robolectric) } diff --git a/demo-project/build.gradle.kts b/demo-project/build.gradle.kts index 04be850..b11ec6d 100644 --- a/demo-project/build.gradle.kts +++ b/demo-project/build.gradle.kts @@ -6,7 +6,12 @@ plugins { testAggregation { modules { - include(projects.demoProject.app, projects.demoProject.domain, projects.demoProject.login) + include( + projects.demoProject.app, + projects.demoProject.domain, + projects.demoProject.login, + projects.demoProject.kmp, + ) exclude(rootProject) } coverage { diff --git a/demo-project/domain/build.gradle.kts b/demo-project/domain/build.gradle.kts index cfb226e..b835d94 100644 --- a/demo-project/domain/build.gradle.kts +++ b/demo-project/domain/build.gradle.kts @@ -6,5 +6,5 @@ plugins { java.toolchain.languageVersion.set(JavaLanguageVersion.of(11)) dependencies { - testImplementation(libs.junit) + testImplementation(libs.kotlin.test) } diff --git a/demo-project/kmp/build.gradle.kts b/demo-project/kmp/build.gradle.kts new file mode 100644 index 0000000..24671c8 --- /dev/null +++ b/demo-project/kmp/build.gradle.kts @@ -0,0 +1,34 @@ +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension +import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootPlugin +import org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension + +plugins { + alias(libs.plugins.android.lib) + alias(libs.plugins.kotlin.multiplatform) +} + +afterEvaluate { + rootProject.the().download = false + rootProject.the().download = false +} + +java.toolchain.languageVersion.set(JavaLanguageVersion.of(11)) + +android { + namespace = "com.example.login" + + compileSdk = libs.versions.android.compileSDK.get().toInt() + defaultConfig { + minSdk = libs.versions.android.minSDK.get().toInt() + } +} + +kotlin { + androidTarget() + jvm() + js { nodejs() } +} + +dependencies { + commonTestImplementation(libs.kotlin.test) +} diff --git a/demo-project/kmp/src/androidMain/kotlin/com/example/kmp/KMPObjectAndroid.kt b/demo-project/kmp/src/androidMain/kotlin/com/example/kmp/KMPObjectAndroid.kt new file mode 100644 index 0000000..9ba4e5d --- /dev/null +++ b/demo-project/kmp/src/androidMain/kotlin/com/example/kmp/KMPObjectAndroid.kt @@ -0,0 +1,7 @@ +package com.example.kmp + +object KMPObjectAndroid { + + val platform = PLATFORM + +} diff --git a/demo-project/kmp/src/androidMain/kotlin/com/example/kmp/Platform.kt b/demo-project/kmp/src/androidMain/kotlin/com/example/kmp/Platform.kt new file mode 100644 index 0000000..10f5cdf --- /dev/null +++ b/demo-project/kmp/src/androidMain/kotlin/com/example/kmp/Platform.kt @@ -0,0 +1,3 @@ +package com.example.kmp + +internal actual val PLATFORM = "android" diff --git a/demo-project/kmp/src/androidUnitTest/kotlin/com/example/kmp/KMPObjectAndroidTest.kt b/demo-project/kmp/src/androidUnitTest/kotlin/com/example/kmp/KMPObjectAndroidTest.kt new file mode 100644 index 0000000..0c66bb6 --- /dev/null +++ b/demo-project/kmp/src/androidUnitTest/kotlin/com/example/kmp/KMPObjectAndroidTest.kt @@ -0,0 +1,13 @@ +package com.example.kmp + +import kotlin.test.Test +import kotlin.test.assertEquals + +class KMPObjectAndroidTest { + + @Test + fun testKMPObject() { + assertEquals(PLATFORM, KMPObjectAndroid.platform) + } + +} diff --git a/demo-project/kmp/src/commonMain/kotlin/com/example/kmp/KMPObject.kt b/demo-project/kmp/src/commonMain/kotlin/com/example/kmp/KMPObject.kt new file mode 100644 index 0000000..1cfa2ec --- /dev/null +++ b/demo-project/kmp/src/commonMain/kotlin/com/example/kmp/KMPObject.kt @@ -0,0 +1,7 @@ +package com.example.kmp + +object KMPObject { + + val platform = PLATFORM + +} diff --git a/demo-project/kmp/src/commonMain/kotlin/com/example/kmp/Platform.kt b/demo-project/kmp/src/commonMain/kotlin/com/example/kmp/Platform.kt new file mode 100644 index 0000000..f6bb36e --- /dev/null +++ b/demo-project/kmp/src/commonMain/kotlin/com/example/kmp/Platform.kt @@ -0,0 +1,3 @@ +package com.example.kmp + +internal expect val PLATFORM: String diff --git a/demo-project/kmp/src/commonTest/kotlin/com/example/kmp/KMPObjectTest.kt b/demo-project/kmp/src/commonTest/kotlin/com/example/kmp/KMPObjectTest.kt new file mode 100644 index 0000000..367f446 --- /dev/null +++ b/demo-project/kmp/src/commonTest/kotlin/com/example/kmp/KMPObjectTest.kt @@ -0,0 +1,13 @@ +package com.example.kmp + +import kotlin.test.Test +import kotlin.test.assertEquals + +class KMPObjectTest { + + @Test + fun testKMPObject() { + assertEquals(PLATFORM, KMPObject.platform) + } + +} diff --git a/demo-project/kmp/src/jsMain/kotlin/com/example/kmp/KMPObjectJS.kt b/demo-project/kmp/src/jsMain/kotlin/com/example/kmp/KMPObjectJS.kt new file mode 100644 index 0000000..4c42e70 --- /dev/null +++ b/demo-project/kmp/src/jsMain/kotlin/com/example/kmp/KMPObjectJS.kt @@ -0,0 +1,7 @@ +package com.example.kmp + +object KMPObjectJS { + + val platform = PLATFORM + +} diff --git a/demo-project/kmp/src/jsMain/kotlin/com/example/kmp/Platform.kt b/demo-project/kmp/src/jsMain/kotlin/com/example/kmp/Platform.kt new file mode 100644 index 0000000..e8aad5e --- /dev/null +++ b/demo-project/kmp/src/jsMain/kotlin/com/example/kmp/Platform.kt @@ -0,0 +1,3 @@ +package com.example.kmp + +internal actual val PLATFORM = "js" diff --git a/demo-project/kmp/src/jsTest/kotlin/com/example/kmp/KMPObjectJSTest.kt b/demo-project/kmp/src/jsTest/kotlin/com/example/kmp/KMPObjectJSTest.kt new file mode 100644 index 0000000..42e3219 --- /dev/null +++ b/demo-project/kmp/src/jsTest/kotlin/com/example/kmp/KMPObjectJSTest.kt @@ -0,0 +1,13 @@ +package com.example.kmp + +import kotlin.test.Test +import kotlin.test.assertEquals + +class KMPObjectJSTest { + + @Test + fun testKMPObject() { + assertEquals(PLATFORM, KMPObjectJS.platform) + } + +} diff --git a/demo-project/kmp/src/jvmMain/kotlin/com/example/kmp/KMPObjectJVM.kt b/demo-project/kmp/src/jvmMain/kotlin/com/example/kmp/KMPObjectJVM.kt new file mode 100644 index 0000000..6454f77 --- /dev/null +++ b/demo-project/kmp/src/jvmMain/kotlin/com/example/kmp/KMPObjectJVM.kt @@ -0,0 +1,7 @@ +package com.example.kmp + +object KMPObjectJVM { + + val platform = PLATFORM + +} diff --git a/demo-project/kmp/src/jvmMain/kotlin/com/example/kmp/Platform.kt b/demo-project/kmp/src/jvmMain/kotlin/com/example/kmp/Platform.kt new file mode 100644 index 0000000..50a4cb1 --- /dev/null +++ b/demo-project/kmp/src/jvmMain/kotlin/com/example/kmp/Platform.kt @@ -0,0 +1,3 @@ +package com.example.kmp + +internal actual val PLATFORM = "jvm" diff --git a/demo-project/kmp/src/jvmTest/kotlin/com/example/kmp/KMPObjectJVMTest.kt b/demo-project/kmp/src/jvmTest/kotlin/com/example/kmp/KMPObjectJVMTest.kt new file mode 100644 index 0000000..e39a0ee --- /dev/null +++ b/demo-project/kmp/src/jvmTest/kotlin/com/example/kmp/KMPObjectJVMTest.kt @@ -0,0 +1,13 @@ +package com.example.kmp + +import kotlin.test.Test +import kotlin.test.assertEquals + +class KMPObjectJVMTest { + + @Test + fun testKMPObject() { + assertEquals(PLATFORM, KMPObjectJVM.platform) + } + +} diff --git a/demo-project/login/build.gradle.kts b/demo-project/login/build.gradle.kts index fa8048d..7ac113b 100644 --- a/demo-project/login/build.gradle.kts +++ b/demo-project/login/build.gradle.kts @@ -43,7 +43,7 @@ dependencies { implementation(libs.androidx.livedata) implementation(libs.androidx.viewmodel) - testImplementation(libs.junit) + testImplementation(libs.kotlin.test) androidTestImplementation(libs.androidx.test.junit) androidTestImplementation(libs.androidx.test.espresso) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f1620df..4e03350 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ androidx-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", ve androidx-test-junit = "androidx.test.ext:junit:1.2.1" androidx-test-espresso = "androidx.test.espresso:espresso-core:3.6.1" google-material = "com.google.android.material:material:1.12.0" -junit = "junit:junit:4.13.2" +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } robolectric = "org.robolectric:robolectric:4.14.1" [plugins] @@ -28,6 +28,7 @@ android-test = { id = "com.android.test", version.ref = "agp" } android-baseline = { id = "androidx.baselineprofile", version = "1.3.3" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } kotlin-samReceiver = { id = "org.jetbrains.kotlin.plugin.sam.with.receiver", version.ref = "kotlin" } gradle-pluginPublish = { id = "com.gradle.plugin-publish", version = "1.3.0" } publicationsReport = { id = "io.github.gmazzo.publications.report", version = "1.2.8" } diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index f020c9b..28d0b65 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -48,12 +48,14 @@ dependencies { compileOnly(gradleKotlinDsl()) compileOnly(plugin(libs.plugins.android)) + compileOnly(plugin(libs.plugins.kotlin.multiplatform)) testImplementation(gradleKotlinDsl()) testImplementation(plugin(libs.plugins.android)) + testImplementation(plugin(libs.plugins.kotlin.multiplatform)) } -testing.suites.withType() { +testing.suites.withType { useKotlinTest(libs.versions.kotlin) } diff --git a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestBaseAggregationPlugin.kt b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestBaseAggregationPlugin.kt index 3ca0c5a..57ebe9d 100644 --- a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestBaseAggregationPlugin.kt +++ b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestBaseAggregationPlugin.kt @@ -2,8 +2,10 @@ package io.github.gmazzo.android.test.aggregation +import com.android.build.api.dsl.BuildType import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.plugins.ExtensionAware import org.gradle.api.provider.Property import org.gradle.kotlin.dsl.aggregateTestCoverage import org.gradle.kotlin.dsl.apply @@ -29,6 +31,13 @@ internal abstract class AndroidTestBaseAggregationPlugin : Plugin { objects.property() ) } + onKotlinJVMTargets { + (this as ExtensionAware).extensions.add( + typeOf>(), + ::aggregateTestCoverage.name, + objects.property() + ) + } } } diff --git a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestCoverageAggregationPlugin.kt b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestCoverageAggregationPlugin.kt index 151a3d2..0dba9ac 100644 --- a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestCoverageAggregationPlugin.kt +++ b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestCoverageAggregationPlugin.kt @@ -17,8 +17,12 @@ import org.gradle.api.attributes.VerificationType import org.gradle.api.file.Directory import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.RegularFile +import org.gradle.api.internal.provider.Providers import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Provider import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.testing.AbstractTestTask import org.gradle.kotlin.dsl.USAGE_TEST_AGGREGATION import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.configure @@ -31,7 +35,10 @@ import org.gradle.kotlin.dsl.property import org.gradle.kotlin.dsl.provideDelegate import org.gradle.kotlin.dsl.registering import org.gradle.kotlin.dsl.the +import org.gradle.kotlin.dsl.invoke import org.gradle.testing.jacoco.plugins.JacocoTaskExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME abstract class AndroidTestCoverageAggregationPlugin : Plugin { @@ -55,7 +62,7 @@ abstract class AndroidTestCoverageAggregationPlugin : Plugin { }) } - configurations.create("codeCoverageExecutionData") { + val codeCoverageExecutionData by configurations.registering { isCanBeConsumed = true isCanBeResolved = false isVisible = false @@ -73,10 +80,7 @@ abstract class AndroidTestCoverageAggregationPlugin : Plugin { } afterEvaluate { jacocoVariants.all variant@{ - val execData = unitTestTaskOf(this@variant)!! - .map { it.the().destinationFile!! } - - outgoing.artifact(execData) { + outgoing.artifact(unitTestTaskOf(this@variant)!!.execData) { type = ArtifactTypeDefinition.BINARY_DATA_TYPE } } @@ -154,6 +158,24 @@ abstract class AndroidTestCoverageAggregationPlugin : Plugin { type = ArtifactTypeDefinition.JVM_CLASS_DIRECTORY } } + + onKotlinJVMTargets variant@{ + val main = compilations[MAIN_COMPILATION_NAME] + + codeCoverageExecutionData.configure { + outgoing.artifact(unitTestTaskOf(this@variant).execData) { + type = ArtifactTypeDefinition.BINARY_DATA_TYPE + } + } + allVariantsSourcesForCoverageReport.configure { + main.allKotlinSourceSets.forAll { + from(it.kotlin.srcDirs) + } + } + allVariantsClassesForCoverageReport.configure { + from(main.output.classesDirs) + } + } } private fun Project.addRobolectricTestsSupport() { @@ -178,4 +200,7 @@ abstract class AndroidTestCoverageAggregationPlugin : Plugin { } } + private val TaskProvider.execData + get() = map { it.the().destinationFile!! } + } diff --git a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestResultsAggregationPlugin.kt b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestResultsAggregationPlugin.kt index 3debf0c..db682ad 100644 --- a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestResultsAggregationPlugin.kt +++ b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/AndroidTestResultsAggregationPlugin.kt @@ -3,11 +3,13 @@ package io.github.gmazzo.android.test.aggregation import com.android.build.api.variant.HasUnitTest import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.attributes.Category import org.gradle.api.attributes.TestSuiteType import org.gradle.api.attributes.Usage import org.gradle.api.attributes.VerificationType import org.gradle.kotlin.dsl.USAGE_TEST_AGGREGATION +import org.gradle.kotlin.dsl.aggregateTestCoverage import org.gradle.kotlin.dsl.apply import org.gradle.kotlin.dsl.named @@ -41,6 +43,14 @@ abstract class AndroidTestResultsAggregationPlugin : Plugin { if (aggregate) listOf(unitTestTaskOf(variant)!!.flatMap { it.binaryResultsDirectory }) else emptyList() }) } + + onKotlinJVMTargets target@{ + testResultsElements.outgoing.artifacts(provider { + val aggregate = this@target.aggregateTestCoverage.getOrElse(true) + + if (aggregate) listOf(unitTestTaskOf(this@target).flatMap { it.binaryResultsDirectory }) else emptyList() + }) + } } } diff --git a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/InternalDSL.kt b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/InternalDSL.kt index bd30d71..5cd8811 100644 --- a/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/InternalDSL.kt +++ b/plugin/src/main/kotlin/io/github/gmazzo/android/test/aggregation/InternalDSL.kt @@ -12,10 +12,16 @@ import org.gradle.kotlin.dsl.aggregateTestCoverage import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.get +import org.gradle.kotlin.dsl.withType import org.gradle.kotlin.dsl.getByName import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.testAggregation import org.gradle.kotlin.dsl.the +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.KotlinTargetsContainer +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataTarget +import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget internal val Project.android get() = the() @@ -62,4 +68,12 @@ private fun TestAggregationExtension.Modules.includes(project: Project) = internal fun Project.unitTestTaskOf(variant: Variant) = (variant as? HasUnitTest) ?.unitTest - ?.let { tasks.named("test${it.name.capitalized()}") } + ?.let { tasks.named("test${it.name.replaceFirstChar { it.uppercase() }}") } + +internal fun Project.unitTestTaskOf(target: KotlinTarget) = + tasks.named("${(target.disambiguationClassifier ?: target.name)}Test") + +internal fun Project.onKotlinJVMTargets(action: KotlinJvmTarget.() -> Unit) = plugins.withId("kotlin-multiplatform") { + the().targets + .withType(action) +} diff --git a/plugin/src/main/kotlin/org/gradle/kotlin/dsl/TestAggregationDSL.kt b/plugin/src/main/kotlin/org/gradle/kotlin/dsl/TestAggregationDSL.kt index 1d23e0f..fb5c379 100644 --- a/plugin/src/main/kotlin/org/gradle/kotlin/dsl/TestAggregationDSL.kt +++ b/plugin/src/main/kotlin/org/gradle/kotlin/dsl/TestAggregationDSL.kt @@ -7,7 +7,9 @@ import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.attributes.Usage +import org.gradle.api.plugins.ExtensionAware import org.gradle.api.provider.Property +import org.jetbrains.kotlin.gradle.plugin.KotlinTarget const val USAGE_TEST_AGGREGATION = "test-aggregation" @@ -26,3 +28,6 @@ val BuildType.aggregateTestCoverage: Property val ProductFlavor.aggregateTestCoverage: Property get() = extensions.getByName>(::aggregateTestCoverage.name) + +val KotlinTarget.aggregateTestCoverage: Property + get() = (this as ExtensionAware).extensions.getByName>(::aggregateTestCoverage.name) diff --git a/settings.gradle.kts b/settings.gradle.kts index c7896f4..bd18878 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,4 +8,5 @@ includeBuild("plugin") include("demo-project:app") include("demo-project:domain") include("demo-project:login") +include("demo-project:kmp") include("demo-project:ui-tests")