From dcf26ae05c0e2f6804d961d4b6db86ed556a37ee Mon Sep 17 00:00:00 2001 From: Yermukhamed Shakhman Date: Thu, 20 Aug 2026 15:31:37 +0200 Subject: [PATCH 1/3] add case: no test sources --- .../gradle/mpp/MppEmptyTestSourceSetsIT.kt | 37 +++++++++++++++++++ .../kotlin/gradle/testbase/testAssertions.kt | 20 ++++++++++ 2 files changed, 57 insertions(+) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt new file mode 100644 index 0000000000000..5197bc13352f1 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt @@ -0,0 +1,37 @@ +/* + * 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.mpp + +import org.gradle.util.GradleVersion +import org.jetbrains.kotlin.gradle.testbase.* +import org.junit.jupiter.api.DisplayName + +@MppGradlePluginTests +@DisplayName("Tests for empty MPP test source sets") +class MppEmptyTestSourceSetsIT : KGPBaseTest() { + + @DisplayName("No test sources in commonTest or jvmTest") + @GradleTest + fun testNoTestSources(gradleVersion: GradleVersion) { + project("base-kotlin-multiplatform-library", gradleVersion) { + buildScriptInjection { + kotlinMultiplatform.jvm() + } + kotlinSourcesDir("commonMain").source("CommonMain.kt") { + """ + package org.example.project + + fun commonFun(): String = "common" + """.trimIndent() + } + + build(":jvmTest") { + assertTasksNoSource(":compileTestKotlinJvm", ":jvmTest") + assertNoTestResultsProduced("jvmTest") + } + } + } +} diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt index 20a8d2bc59b65..c7cd550a8dfd8 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt @@ -13,12 +13,14 @@ import org.jdom.Content import org.jdom.Element import org.jdom.Text import org.jetbrains.kotlin.test.util.trimTrailingWhitespaces +import java.nio.file.Files import java.nio.file.Path import java.util.Base64 import kotlin.io.path.absolutePathString import kotlin.io.path.name import kotlin.io.path.readText import kotlin.test.assertEquals +import kotlin.test.assertTrue /** * @param stripBrowserVersionInfoFromTestCaseNames Some test executor implementations include browser version info in test case names, @@ -62,6 +64,24 @@ fun GradleProject.assertTestResults( assertEquals(expectedTestResults, actualTestResults) } +fun GradleProject.assertNoTestResultsProduced( + taskName: String, + subprojectName: String? = null, +) { + val dirs = testResultsAndReportsDirs(taskName, subprojectName) + + for (dir in listOf(dirs.first, dirs.second)) { + if (Files.exists(dir)) { + val entries = Files.list(dir).use { it.toList() } + .filter { it.name != "binary" } + assertTrue( + entries.isEmpty(), + "Expected directory '$dir' to be absent or contain no test results, but found entries: ${entries.joinToString()}" + ) + } + } +} + internal fun readValidateAndCleanupTestResults( testReportDirs: List, projectPath: Path, From e0b4cb17e6355e9e08aaa4bb460330e37b5c3d1d Mon Sep 17 00:00:00 2001 From: Yermukhamed Shakhman Date: Thu, 20 Aug 2026 15:40:01 +0200 Subject: [PATCH 2/3] add case: sources without tests --- .../gradle/mpp/MppEmptyTestSourceSetsIT.kt | 28 +++++++++++++++++++ .../kotlin/gradle/testbase/testAssertions.kt | 18 +++++------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt index 5197bc13352f1..02a260abc2579 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt @@ -34,4 +34,32 @@ class MppEmptyTestSourceSetsIT : KGPBaseTest() { } } } + + // In Gradle 9.0+, executing a test task fails by default when test compilation outputs exist but 0 tests are discovered. + // Older Gradle versions succeeded silently. + @DisplayName("Sources in jvmTest without test cases fail on Gradle 9.0+") + @GradleTestVersions(minVersion = TestVersions.Gradle.G_9_0) + @GradleTest + fun testSourcesWithoutTests(gradleVersion: GradleVersion) { + project("base-kotlin-multiplatform-library", gradleVersion) { + buildScriptInjection { + kotlinMultiplatform.jvm() + } + kotlinSourcesDir("jvmTest").source("NotATest.kt") { + """ + package org.example.project + + class NotATest { + fun helper(): String = "helper" + } + """.trimIndent() + } + + buildAndFail(":jvmTest") { + assertTasksExecuted(":compileTestKotlinJvm") + assertTasksFailed(":jvmTest") + assertNoTestResultsProduced("jvmTest") + } + } + } } diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt index c7cd550a8dfd8..a3f2c4df5eddb 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/testbase/testAssertions.kt @@ -68,17 +68,13 @@ fun GradleProject.assertNoTestResultsProduced( taskName: String, subprojectName: String? = null, ) { - val dirs = testResultsAndReportsDirs(taskName, subprojectName) - - for (dir in listOf(dirs.first, dirs.second)) { - if (Files.exists(dir)) { - val entries = Files.list(dir).use { it.toList() } - .filter { it.name != "binary" } - assertTrue( - entries.isEmpty(), - "Expected directory '$dir' to be absent or contain no test results, but found entries: ${entries.joinToString()}" - ) - } + val testResultsDir = testResultsAndReportsDirs(taskName, subprojectName).first + if (Files.exists(testResultsDir)) { + val xmlFiles = testResultsDir.allFilesWithExtension("xml") + assertTrue( + xmlFiles.isEmpty(), + "Expected no test result XML files in '$testResultsDir', but found: ${xmlFiles.joinToString()}" + ) } } From 0885f9c3ae5b8cb60e9871f12debd51c5e9c3ab0 Mon Sep 17 00:00:00 2001 From: Yermukhamed Shakhman Date: Thu, 20 Aug 2026 16:04:53 +0200 Subject: [PATCH 3/3] add case: common test discovery and inheritence --- .../gradle/mpp/MppEmptyTestSourceSetsIT.kt | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt index 02a260abc2579..90d079d4285ea 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppEmptyTestSourceSetsIT.kt @@ -62,4 +62,63 @@ class MppEmptyTestSourceSetsIT : KGPBaseTest() { } } } + + @DisplayName("Common test discovery and inheritance in jvmTest") + @GradleTest + fun testCommonTestDiscoveryAndInheritance(gradleVersion: GradleVersion) { + project("base-kotlin-multiplatform-library", gradleVersion) { + buildScriptInjection { + kotlinMultiplatform.jvm() + kotlinMultiplatform.sourceSets.getByName("commonTest").dependencies { + implementation(kotlin("test")) + } + } + kotlinSourcesDir("commonTest").source("CommonSampleTest.kt") { + """ + package org.example.project + + import kotlin.test.Test + import kotlin.test.assertTrue + + open class CommonSampleTest { + @Test + fun testInCommon() { + assertTrue(true) + } + } + """.trimIndent() + } + + build(":jvmTest") { + assertTasksExecuted(":compileTestKotlinJvm", ":jvmTest") + assertExecutedTestCases("jvmTest", "org.example.project.CommonSampleTest#testInCommon") + } + + kotlinSourcesDir("jvmTest").source("JvmSampleTest.kt") { + """ + package org.example.project + + import kotlin.test.Test + import kotlin.test.assertTrue + + class JvmSampleTest : CommonSampleTest() { + @Test + fun testInJvm() { + assertTrue(true) + } + } + """.trimIndent() + } + + build(":jvmTest") { + assertTasksExecuted(":compileTestKotlinJvm", ":jvmTest") + assertExecutedTestCases( + "jvmTest", + "org.example.project.CommonSampleTest#testInCommon", + "org.example.project.JvmSampleTest#testInCommon", + "org.example.project.JvmSampleTest#testInJvm", + ) + } + } + } }