From c204fcf10c930a488acdd40df88e1d510db9d8d0 Mon Sep 17 00:00:00 2001 From: Yermukhamed Shakhman Date: Thu, 20 Aug 2026 18:49:48 +0200 Subject: [PATCH 1/4] add test case: kmp test aggregation ":allTests" --- .../kotlin/gradle/mpp/MppTestReportIT.kt | 75 +++++++++++++++++++ .../kotlin/gradle/testbase/testAssertions.kt | 4 +- 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt new file mode 100644 index 0000000000000..24e3bf969a072 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt @@ -0,0 +1,75 @@ +/* + * 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 +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +@MppGradlePluginTests +@DisplayName("Tests for Multiplatform test reporting") +class MppTestReportIT : KGPBaseTest() { + + @DisplayName("Aggregated test report contains results from multiple targets (:allTests)") + @GradleTest + fun testAllTestsReportAggregation(gradleVersion: GradleVersion) { + project( + "base-kotlin-multiplatform-library", + gradleVersion, + buildOptions = defaultBuildOptions.disableIsolatedProjectsBecauseOfJsAndWasmKT75899(), + ) { + buildScriptInjection { + kotlinMultiplatform.jvm() + kotlinMultiplatform.js { + nodejs() + } + kotlinMultiplatform.sourceSets.getByName("commonTest").dependencies { + implementation(kotlin("test")) + } + } + + kotlinSourcesDir("commonTest").source("org/example/project/SampleTest.kt") { + """ + package org.example.project + + import kotlin.test.Test + import kotlin.test.assertTrue + + class SampleTest { + @Test + fun testOne() { + assertTrue(true) + } + + @Test + fun testTwo() { + assertTrue(true) + } + } + """.trimIndent() + } + + build(":allTests") { + assertTasksExecuted(":jvmTest", ":jsNodeTest", ":allTests") + } + + assertFileInProjectExists("build/reports/tests/allTests/index.html") + assertExecutedTestCases( + ":jvmTest", + "org.example.project.SampleTest#testOne", + "org.example.project.SampleTest#testTwo", + ) + assertExecutedTestCases( + ":jsNodeTest", + "org.example.project.SampleTest#testOne", + "org.example.project.SampleTest#testTwo", + ) + } + } +} 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 5ff5ea54e0559..bd443432009f8 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 @@ -142,6 +142,7 @@ fun GradleProject.readTestCases( taskName: String, subprojectName: String? = null, ): List { + val simpleTaskName = taskName.removePrefix(":").substringAfterLast(':') val testReportDir = testResultsAndReportsDirs(taskName, subprojectName).first if (!Files.exists(testReportDir)) { @@ -157,9 +158,10 @@ fun GradleProject.readTestCases( else -> root.getChildren("testcase") } testCases.map { testCaseElement -> - val className = testCaseElement.getAttributeValue("classname") + val rawClassName = testCaseElement.getAttributeValue("classname") ?: testCaseElement.getAttributeValue("className") ?: "" + val className = rawClassName.removePrefix("$simpleTaskName.") val name = testCaseElement.getAttributeValue("name") ?: "" val failureElement = testCaseElement.getChild("failure") ?: testCaseElement.getChild("error") val failure = failureElement?.let { From 804ee9ea37876e9cb2e13249f8e9e5bc296bb732 Mon Sep 17 00:00:00 2001 From: Yermukhamed Shakhman Date: Thu, 20 Aug 2026 18:51:01 +0200 Subject: [PATCH 2/4] add test case: fixture with lightweight jvm + js setup with disabled isloated projects --- .../kotlin/gradle/mpp/MppTestReportIT.kt | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt index 24e3bf969a072..2d2f708cb1056 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt @@ -72,4 +72,61 @@ class MppTestReportIT : KGPBaseTest() { ) } } + + @DisplayName("Test failure details, stack trace anti-drift, and HTML report (:jvmTest)") + @GradleTest + fun testTestFailureReportingAndStackTrace(gradleVersion: GradleVersion) { + project( + "base-kotlin-multiplatform-library", + gradleVersion, + buildOptions = defaultBuildOptions.disableIsolatedProjectsBecauseOfJsAndWasmKT75899(), + ) { + buildScriptInjection { + kotlinMultiplatform.jvm() + kotlinMultiplatform.sourceSets.getByName("commonTest").dependencies { + implementation(kotlin("test")) + } + } + + val testClass = "FailingTest" + val testPackage = "org.example.project" + val testSource = """ + package $testPackage + + import kotlin.test.Test + + class $testClass { + @Test + fun failing() { + throw IllegalStateException("boom") + } + } + """.trimIndent() + + val throwingLine = testSource.lines().indexOfFirst { "throw" in it } + 1 + + kotlinSourcesDir("commonTest").source("$testPackage/$testClass.kt") { + testSource + } + + buildAndFail(":jvmTest") { + assertTasksFailed(":jvmTest") + } + + val testCases = readTestCases(":jvmTest") + val failingTestCase = testCases.single { it.className == "$testPackage.$testClass" && it.name.startsWith("failing") } + val failure = failingTestCase.failure + assertNotNull(failure, "Expected failure information for test case") + assertEquals("java.lang.IllegalStateException", failure.type) + assertEquals("java.lang.IllegalStateException: boom", failure.message) + assertNotNull(failure.stackTrace, "Expected stack trace in test failure") + assertTrue( + failure.stackTrace.contains("$testPackage.$testClass.failing($testClass.kt:$throwingLine)"), + "Expected stack trace to contain '$testPackage.$testClass.failing($testClass.kt:$throwingLine)', but was:\n${failure.stackTrace}" + ) + + val htmlReport = testClassHtmlReport(":jvmTest", "$testPackage.$testClass", gradleVersion, targetName = "jvm") + assertFileExists(htmlReport) + } + } } From d3166091410fa72144e0d87e93dd04822af36a24 Mon Sep 17 00:00:00 2001 From: Yermukhamed Shakhman Date: Fri, 21 Aug 2026 13:54:46 +0200 Subject: [PATCH 3/4] add Smoke test annotation because it can be affected by other domains --- .../kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt index 2d2f708cb1056..ef9d2d109bf4c 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/mpp/MppTestReportIT.kt @@ -7,13 +7,15 @@ package org.jetbrains.kotlin.gradle.mpp import org.gradle.util.GradleVersion import org.jetbrains.kotlin.gradle.testbase.* +import org.jetbrains.kotlin.testFederation.SmokeTest import org.junit.jupiter.api.DisplayName import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue @MppGradlePluginTests -@DisplayName("Tests for Multiplatform test reporting") +@SmokeTest +@DisplayName("Tests for KMP test reporting") class MppTestReportIT : KGPBaseTest() { @DisplayName("Aggregated test report contains results from multiple targets (:allTests)") From 151b67bf7892a4dc28cdb2299ab11ea9abbbc924 Mon Sep 17 00:00:00 2001 From: Yermukhamed Shakhman Date: Fri, 21 Aug 2026 16:16:58 +0200 Subject: [PATCH 4/4] remove redundant .removePrefix(..) method Co-authored-by: Egor Andreevich --- .../org/jetbrains/kotlin/gradle/testbase/testAssertions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bd443432009f8..b5820eaa36e8f 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 @@ -142,7 +142,7 @@ fun GradleProject.readTestCases( taskName: String, subprojectName: String? = null, ): List { - val simpleTaskName = taskName.removePrefix(":").substringAfterLast(':') + val simpleTaskName = taskName.substringAfterLast(':') val testReportDir = testResultsAndReportsDirs(taskName, subprojectName).first if (!Files.exists(testReportDir)) {