From 6ca63aa6ab4f6f255ac3a02a43e0c1c11fb694bd Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 23 Apr 2024 17:19:27 +0200 Subject: [PATCH 01/31] Part of KT-64200 - Migrate the CLI integration tests away from the custom integration test convention (which are not build-cache compatible) to use JVM Test Suites. - Remove shadowing, replace with passing in the JARs via system properties. (a custom CommandLineArgumentProvider is confusing and overly complicated, but it's required to satisfy Gradle input normalization, so that the build-cache can be re-used.) - Simplify `processUtils.kt` - the coroutines logic isn't necessary since the entrypoint just used `runBlocking {}`. - move `jsonBuilder.kt` test-util into `src/main`, since it's not a test - Rename `CliIntegrationTest` to `CliTest` (it wasn't an integration test, and the name was confusing compared to the actual `CliIntegrationTest`) --- ...okkabuild.test-cli-dependencies.gradle.kts | 42 +++++++ dokka-integration-tests/cli/build.gradle.kts | 113 +++++++++--------- .../dokka/it/cli/CliIntegrationTest.kt | 110 ++++++++++------- .../it/cli/AbstractCliIntegrationTest.kt | 35 +----- .../org/jetbrains/dokka/it/cli/jsonBuilder.kt | 16 ++- .../org/jetbrains/dokka/it/processUtils.kt | 50 ++------ .../jetbrains/dokka/it/systemProperties.kt | 8 ++ .../kotlin/org/jetbrains/dokka/CliTest.kt | 2 +- .../plugin-base/build.gradle.kts | 8 +- 9 files changed, 203 insertions(+), 181 deletions(-) create mode 100644 build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts rename dokka-integration-tests/cli/src/{integrationTest => main}/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt (77%) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts new file mode 100644 index 0000000000..dbeb425b0d --- /dev/null +++ b/build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts @@ -0,0 +1,42 @@ + /* + * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +import dokkabuild.utils.declarable +import dokkabuild.utils.resolvable +import org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE +import org.gradle.api.attributes.Bundling.SHADOWED +import org.gradle.api.attributes.Usage.JAVA_RUNTIME +import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE + + +val dokkaCli: Configuration by configurations.creating { + description = "Dependency on Dokka CLI JAR. Must only contain a single dependency." + declarable() +} + +val dokkaCliResolver: Configuration by configurations.creating { + description = "Resolve the Dokka CLI JAR. Intransitive - must only contain a single JAR." + resolvable() + extendsFrom(dokkaCli) + attributes { + attribute(USAGE_ATTRIBUTE, objects.named(JAVA_RUNTIME)) + attribute(BUNDLING_ATTRIBUTE, objects.named(SHADOWED)) + } + // we should have single artifact here + isTransitive = false +} + + +val dokkaPluginsClasspath: Configuration by configurations.creating { + description = "Dokka CLI runtime dependencies required to run Dokka CLI, and its plugins." + declarable() +} + +val dokkaPluginsClasspathResolver: Configuration by configurations.creating { + description = "Resolve Dokka CLI runtime dependencies required to run Dokka CLI, and its plugins." + resolvable() + extendsFrom(dokkaPluginsClasspath) + attributes { + attribute(USAGE_ATTRIBUTE, objects.named(JAVA_RUNTIME)) + } +} diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 7caa3569fe..5c9b3c1f62 100644 --- a/dokka-integration-tests/cli/build.gradle.kts +++ b/dokka-integration-tests/cli/build.gradle.kts @@ -1,79 +1,84 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +@file:Suppress("UnstableApiUsage") -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE +import org.gradle.api.attributes.Bundling.SHADOWED plugins { - id("dokkabuild.test-integration") - id("com.github.johnrengelman.shadow") + id("dokkabuild.kotlin-jvm") + id("dokkabuild.test-cli-dependencies") + `jvm-test-suite` } dependencies { - implementation(kotlin("test-junit5")) - implementation(libs.junit.jupiterApi) - implementation(projects.utilities) -} + api(kotlin("test-junit5")) + api(libs.junit.jupiterApi) + api(projects.utilities) -val cliPluginsClasspath: Configuration by configurations.creating { - description = "plugins/dependencies required to run CLI with base plugin" - attributes { - attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME)) - } + dokkaCli("org.jetbrains.dokka:runner-cli") - // we don't fetch transitive dependencies here to be able to control external dependencies explicitly - isTransitive = false -} + dokkaPluginsClasspath("org.jetbrains.dokka:plugin-base") + dokkaPluginsClasspath(libs.kotlinx.html) + dokkaPluginsClasspath(libs.freemarker) -val cliClasspath: Configuration by configurations.creating { - description = "dependency on CLI JAR" - attributes { - attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage.JAVA_RUNTIME)) - attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED)) + val analysisDependency = dokkaBuild.integrationTestUseK2.map { useK2 -> + if (useK2) { + "org.jetbrains.dokka:analysis-kotlin-symbols" + } else { + "org.jetbrains.dokka:analysis-kotlin-descriptors" + } + } + dokkaPluginsClasspath(analysisDependency) { + attributes { + attribute(BUNDLING_ATTRIBUTE, project.objects.named(SHADOWED)) + } } - // we should have single artifact here - isTransitive = false } -dependencies { - cliClasspath("org.jetbrains.dokka:runner-cli") - - cliPluginsClasspath("org.jetbrains.dokka:plugin-base") - // required dependencies of `plugin-base` - cliPluginsClasspath(libs.freemarker) - cliPluginsClasspath(libs.kotlinx.html) +/** + * Provide files required for running Dokka CLI in a build cache friendly way. + */ +abstract class DokkaCliClasspathProvider : CommandLineArgumentProvider { + @get:Classpath + abstract val dokkaCli: RegularFileProperty - val tryK2 = project.providers - .gradleProperty("org.jetbrains.dokka.experimental.tryK2") - .map(String::toBoolean) - .orNull ?: false + @get:Classpath + abstract val dokkaPluginsClasspath: ConfigurableFileCollection - val analysisDependency = when { - tryK2 -> "org.jetbrains.dokka:analysis-kotlin-symbols" - else -> "org.jetbrains.dokka:analysis-kotlin-descriptors" + override fun asArguments(): Iterable = buildList { + add("-D" + "dokkaCliJarPath=" + dokkaCli.asFile.get().absolutePath) + add("-D" + "dokkaPluginsClasspath=" + dokkaPluginsClasspath.joinToString(";") { it.absolutePath }) } +} - cliPluginsClasspath(analysisDependency) { - attributes { - attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling.SHADOWED)) + +testing { + suites { + withType().configureEach { + useJUnitJupiter() } - } -} -val cliPluginsShadowJar by tasks.registering(ShadowJar::class) { - archiveFileName.set("cli-plugins-${project.version}.jar") - configurations = listOf(cliPluginsClasspath) + register("integrationTest") { + dependencies { + implementation(project()) + } - // service files are merged to make sure all Dokka plugins - // from the dependencies are loaded, and not just a single one. - mergeServiceFiles() + targets.configureEach { + testTask.configure { + jvmArgumentProviders.add( + objects.newInstance().apply { + dokkaCli = configurations.dokkaCliResolver.map { it.singleFile } + dokkaPluginsClasspath.from(configurations.dokkaPluginsClasspathResolver) + } + ) + } + } + } + } } -tasks.integrationTest { - dependsOn(cliClasspath) - dependsOn(cliPluginsShadowJar) - - inputs.dir(file("projects")) - environment("CLI_JAR_PATH", cliClasspath.singleFile) - environment("BASE_PLUGIN_JAR_PATH", cliPluginsShadowJar.get().archiveFile.get()) +tasks.check { + dependsOn(testing.suites) } diff --git a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt b/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt index b9c29dc57b..143a556bdb 100644 --- a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt +++ b/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt @@ -1,13 +1,10 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ - package org.jetbrains.dokka.it.cli import org.jetbrains.dokka.it.awaitProcessResult import java.io.File -import java.io.PrintWriter -import java.lang.IllegalStateException import kotlin.test.* class CliIntegrationTest : AbstractCliIntegrationTest() { @@ -20,7 +17,11 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { @Test fun runHelp() { - val process = ProcessBuilder("java", "-jar", cliJarFile.path, "-h") + val process = ProcessBuilder( + "java", + "-jar", dokkaCliJarPath, + "-h", + ) .redirectErrorStream(true) .start() @@ -34,9 +35,10 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, + "java", + "-jar", dokkaCliJarPath, "-outputDir", dokkaOutputDir.path, - "-pluginsClasspath", basePluginJarFile.path, + "-pluginsClasspath", dokkaPluginsClasspath, "-moduleName", "Basic Project", "-sourceSet", buildString { @@ -48,6 +50,7 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { append(" -skipDeprecated") } ) + .directory(projectDir) .redirectErrorStream(true) .start() @@ -109,9 +112,10 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, + "java", + "-jar", dokkaCliJarPath, "-outputDir", dokkaOutputDir.path, - "-pluginsClasspath", basePluginJarFile.path, + "-pluginsClasspath", dokkaPluginsClasspath, "-moduleName", "Basic Project", "-failOnWarning", "-sourceSet", @@ -137,9 +141,10 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, + "java", + "-jar", dokkaCliJarPath, "-outputDir", dokkaOutputDir.path, - "-pluginsClasspath", basePluginJarFile.path, + "-pluginsClasspath", dokkaPluginsClasspath, "-moduleName", "Basic Project", "-sourceSet", buildString { @@ -167,10 +172,11 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, + "java", + "-jar", dokkaCliJarPath, "-outputDir", dokkaOutputDir.path, "-loggingLevel", "DEBUG", - "-pluginsClasspath", basePluginJarFile.path, + "-pluginsClasspath", dokkaPluginsClasspath, "-sourceSet", buildString { append(" -src ${File(projectDir, "src").path}") @@ -203,10 +209,11 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, + "java", + "-jar", dokkaCliJarPath, "-outputDir", dokkaOutputDir.path, "-loggingLevel", "WARN", - "-pluginsClasspath", basePluginJarFile.path, + "-pluginsClasspath", dokkaPluginsClasspath, "-sourceSet", buildString { append(" -src ${File(projectDir, "src").path}") @@ -225,9 +232,10 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, + "java", + "-jar", dokkaCliJarPath, "-outputDir", dokkaOutputDir.path, - "-pluginsClasspath", basePluginJarFile.path, + "-pluginsClasspath", dokkaPluginsClasspath, "-moduleName", "Basic Project", "-sourceSet", buildString { @@ -266,20 +274,26 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { ) } - @Test fun `should accept json as input configuration`() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) - val resourcePath = javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") - val jsonPath = File(resourcePath).absolutePath - PrintWriter(jsonPath).run { - write(jsonBuilder(dokkaOutputDir.invariantSeparatorsPath, basePluginJarFile.invariantSeparatorsPath, File(projectDir, "src").invariantSeparatorsPath, reportUndocumented = true)) - close() - } + val resourcePath = + javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") + val jsonPath = File(resourcePath) + jsonPath.writeText( + jsonBuilder( + outputPath = dokkaOutputDir.invariantSeparatorsPath, + pluginsClasspath = dokkaPluginsClasspath, + projectPath = File(projectDir, "src").invariantSeparatorsPath, + reportUndocumented = true + ) + ) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, jsonPath + "java", + "-jar", dokkaCliJarPath, + jsonPath.absolutePath, ).redirectErrorStream(true).start() val result = process.awaitProcessResult() @@ -304,29 +318,29 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { } /** - * This test disables global `reportUndocumneted` property and set `reportUndocumented` via perPackageOptions to + * This test disables global `reportUndocumented` property and set `reportUndocumented` via `perPackageOptions` to * make sure that global settings apply to dokka context. */ @Test fun `global settings should overwrite package options in configuration`() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) - val resourcePath = javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") - val jsonPath = File(resourcePath).absolutePath - PrintWriter(jsonPath).run { - write( - jsonBuilder( - outputPath = dokkaOutputDir.invariantSeparatorsPath, - pluginsClasspath = basePluginJarFile.invariantSeparatorsPath, - projectPath = File(projectDir, "src").invariantSeparatorsPath, - globalSourceLinks = """ + val resourcePath = + javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") + val jsonPath = File(resourcePath) + jsonPath.writeText( + jsonBuilder( + outputPath = dokkaOutputDir.invariantSeparatorsPath, + pluginsClasspath = dokkaPluginsClasspath, + projectPath = File(projectDir, "src").invariantSeparatorsPath, + globalSourceLinks = """ { "localDirectory": "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin", "remoteUrl": "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin", "remoteLineSuffix": "#L" } """.trimIndent(), - globalExternalDocumentationLinks = """ + globalExternalDocumentationLinks = """ { "url": "https://docs.oracle.com/javase/8/docs/api/", "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list" @@ -336,7 +350,7 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { "packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list" } """.trimIndent(), - globalPerPackageOptions = """ + globalPerPackageOptions = """ { "matchingRegex": ".*", "skipDeprecated": "true", @@ -344,15 +358,17 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { "documentedVisibilities": ["PUBLIC", "PRIVATE", "PROTECTED", "INTERNAL", "PACKAGE"] } """.trimIndent(), - reportUndocumented = false - ), - ) - close() - } + reportUndocumented = false + ), + ) val process = ProcessBuilder( - "java", "-jar", cliJarFile.path, jsonPath - ).redirectErrorStream(true).start() + "java", + "-jar", dokkaCliJarPath, + jsonPath.absolutePath, + ) + .redirectErrorStream(true) + .start() val result = process.awaitProcessResult() assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") @@ -376,7 +392,7 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { } @Test - fun `relative paths in configuraiton should work`() { + fun `relative paths in configuration should work`() { val resourcePath = javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") val jsonPath = File(resourcePath) @@ -386,13 +402,15 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { jsonPath.writeText( jsonBuilder( outputPath = dokkaOutputDir.invariantSeparatorsPath, - pluginsClasspath = basePluginJarFile.absoluteFile.invariantSeparatorsPath, + pluginsClasspath = dokkaPluginsClasspath, projectPath = "src", // relative path ) ) ProcessBuilder( - "java", "-jar", cliJarFile.absolutePath, jsonPath.absolutePath + "java", + "-jar", dokkaCliJarPath, + jsonPath.absolutePath ).directory(projectDir).redirectErrorStream(true).start().also { process -> val result = process.awaitProcessResult() assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") diff --git a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt index 70c69753dd..14409663cc 100644 --- a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt +++ b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt @@ -1,40 +1,15 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ - package org.jetbrains.dokka.it.cli import org.jetbrains.dokka.it.AbstractIntegrationTest -import java.io.File -import kotlin.test.BeforeTest -import kotlin.test.assertTrue +import org.jetbrains.dokka.it.systemProperty public abstract class AbstractCliIntegrationTest : AbstractIntegrationTest() { + /** The Dokka CLI JAR. */ + protected val dokkaCliJarPath: String by systemProperty() - protected val cliJarFile: File by lazy { - File(tempFolder, "dokka.jar") - } - - protected val basePluginJarFile: File by lazy { - File(tempFolder, "base-plugin.jar") - } - - @BeforeTest - public fun copyJarFiles() { - val cliJarPathEnvironmentKey = "CLI_JAR_PATH" - val cliJarFile = File(System.getenv(cliJarPathEnvironmentKey)) - assertTrue( - cliJarFile.exists() && cliJarFile.isFile, - "Missing path to CLI jar System.getenv($cliJarPathEnvironmentKey)" - ) - cliJarFile.copyTo(this.cliJarFile) - - val basePluginPathEnvironmentKey = "BASE_PLUGIN_JAR_PATH" - val basePluginJarFile = File(System.getenv(basePluginPathEnvironmentKey)) - assertTrue( - basePluginJarFile.exists() && basePluginJarFile.isFile, - "Missing path to base plugin jar System.getenv($basePluginPathEnvironmentKey)" - ) - basePluginJarFile.copyTo(this.basePluginJarFile) - } + /** Classpath required for running the Dokka CLI, delimited by `;`. */ + protected val dokkaPluginsClasspath: String by systemProperty() } diff --git a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt similarity index 77% rename from dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt rename to dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt index 4ab894432a..a14cb462ca 100644 --- a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt +++ b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt @@ -4,21 +4,27 @@ package org.jetbrains.dokka.it.cli -fun jsonBuilder( +import org.intellij.lang.annotations.Language + +@Language("JSON") +public fun jsonBuilder( outputPath: String, pluginsClasspath: String, projectPath: String, + @Language("JSON", prefix = "[", suffix = "]") globalSourceLinks: String = "", + @Language("JSON", prefix = "[", suffix = "]") globalExternalDocumentationLinks: String = "", + @Language("JSON", prefix = "[", suffix = "]") globalPerPackageOptions: String = "", - reportUndocumented: Boolean = false - + reportUndocumented: Boolean = false, ): String { - return """{ + return """ +{ "moduleName": "Dokka Example", "moduleVersion": null, "outputDir": "$outputPath", - "pluginsClasspath": ["$pluginsClasspath"], + "pluginsClasspath": [${pluginsClasspath.split(";").joinToString(",") { "\"$it\"" }}], "cacheRoot": null, "offlineMode": false, "sourceLinks": [$globalSourceLinks], diff --git a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt index 23d3941d29..9ae4036f69 100644 --- a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt +++ b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt @@ -1,55 +1,23 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ - package org.jetbrains.dokka.it -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking -import kotlin.concurrent.thread +import java.util.concurrent.TimeUnit class ProcessResult( val exitCode: Int, val output: String ) -fun Process.awaitProcessResult(): ProcessResult = runBlocking { - val exitCode = async { awaitExitCode() } - val output = async { awaitOutput() } - ProcessResult( - exitCode.await(), - output.await() - ) -} +fun Process.awaitProcessResult(): ProcessResult { + waitFor(60, TimeUnit.SECONDS) -private suspend fun Process.awaitExitCode(): Int { - val deferred = CompletableDeferred() - thread { - try { - deferred.complete(this.waitFor()) - } catch (e: Throwable) { - deferred.completeExceptionally(e) - } - } + val output = inputStream.reader().readText() + println(output) - return deferred.await() -} - -private suspend fun Process.awaitOutput(): String { - val deferred = CompletableDeferred() - thread { - try { - var string = "" - this.inputStream.bufferedReader().forEachLine { line -> - println(line) - string += line + System.lineSeparator() - } - deferred.complete(string) - } catch (e: Throwable) { - deferred.completeExceptionally(e) - } - } - - return deferred.await() + return ProcessResult( + exitCode = exitValue(), + output = output, + ) } diff --git a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt index 24f43e52d4..74caf38078 100644 --- a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt +++ b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt @@ -6,6 +6,14 @@ package org.jetbrains.dokka.it import kotlin.properties.ReadOnlyProperty +/** + * Delegated accessor for a system property. + * + * @see System.getProperty + */ +fun systemProperty(): ReadOnlyProperty = + systemProperty { it } + /** * Delegated accessor for a system property. * diff --git a/dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt b/dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt index 04757fae34..e69d6ea47d 100644 --- a/dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt +++ b/dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt @@ -9,7 +9,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class CliIntegrationTest { +class CliTest { @Test fun `should apply global settings to all source sets`() { diff --git a/dokka-subprojects/plugin-base/build.gradle.kts b/dokka-subprojects/plugin-base/build.gradle.kts index 753ee31eb3..58dc8d6764 100644 --- a/dokka-subprojects/plugin-base/build.gradle.kts +++ b/dokka-subprojects/plugin-base/build.gradle.kts @@ -55,11 +55,11 @@ dependencies { } } - // access the frontend files via the dependency on :plugins:base:frontend - val dokkaHtmlFrontendFiles: Provider = +// access the frontend files via the dependency on :plugins:base:frontend +val dokkaHtmlFrontendFiles: Provider = configurations.dokkaHtmlFrontendFiles.map { frontendFiles -> - frontendFiles.incoming.artifacts.artifactFiles - } + frontendFiles.incoming.artifacts.artifactFiles + } val prepareDokkaHtmlFrontendFiles by tasks.registering(Sync::class) { description = "copy Dokka Base frontend files into the resources directory" From 0059b5f975a7d88ee8f3fde739343b5d7a1059f7 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:33:05 +0200 Subject: [PATCH 02/31] fix dokkaCli dependency resolution --- build.gradle.kts | 52 ++++++++++++-------- dokka-integration-tests/cli/build.gradle.kts | 9 ++-- dokka-integration-tests/settings.gradle.kts | 2 +- 3 files changed, 39 insertions(+), 24 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2a6ac68dc2..887b0f2a0b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,35 +11,43 @@ val gradlePluginIncludedBuilds = listOf("runner-gradle-plugin-classic") addDependencyOnSameTasksOfIncludedBuilds("assemble", "build", "clean", "check") -registerParentGroupTasks("publishing", taskNames = listOf( - "publishAllPublicationsToMavenCentralRepository", - "publishAllPublicationsToProjectLocalRepository", - "publishAllPublicationsToSnapshotRepository", - "publishAllPublicationsToSpaceDevRepository", - "publishAllPublicationsToSpaceTestRepository", - "publishToMavenLocal" -)) { +registerParentGroupTasks( + "publishing", taskNames = listOf( + "publishAllPublicationsToMavenCentralRepository", + "publishAllPublicationsToProjectLocalRepository", + "publishAllPublicationsToSnapshotRepository", + "publishAllPublicationsToSpaceDevRepository", + "publishAllPublicationsToSpaceTestRepository", + "publishToMavenLocal" + ) +) { it.name in publishedIncludedBuilds } -registerParentGroupTasks("gradle plugin", taskNames = listOf( - "publishPlugins", - "validatePlugins" -)) { +registerParentGroupTasks( + "gradle plugin", taskNames = listOf( + "publishPlugins", + "validatePlugins" + ) +) { it.name in gradlePluginIncludedBuilds } -registerParentGroupTasks("bcv", taskNames = listOf( - "apiDump", - "apiCheck", - "apiBuild" -)) { +registerParentGroupTasks( + "bcv", taskNames = listOf( + "apiDump", + "apiCheck", + "apiBuild" + ) +) { it.name in publishedIncludedBuilds } -registerParentGroupTasks("verification", taskNames = listOf( - "test" -)) +registerParentGroupTasks( + "verification", taskNames = listOf( + "test" + ) +) tasks.register("integrationTest") { group = "verification" @@ -82,3 +90,7 @@ fun includedBuildTasks(taskName: String, filter: (IncludedBuild) -> Boolean = { .filter { it.name != "build-logic" } .filter(filter) .mapNotNull { it.task(":$taskName") } + +tasks.check { + gradle.includedBuilds.forEach { dependsOn(it.task(":check")) } +} diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 5c9b3c1f62..625633dbb3 100644 --- a/dokka-integration-tests/cli/build.gradle.kts +++ b/dokka-integration-tests/cli/build.gradle.kts @@ -42,13 +42,16 @@ dependencies { */ abstract class DokkaCliClasspathProvider : CommandLineArgumentProvider { @get:Classpath - abstract val dokkaCli: RegularFileProperty + abstract val dokkaCli: ConfigurableFileCollection @get:Classpath abstract val dokkaPluginsClasspath: ConfigurableFileCollection override fun asArguments(): Iterable = buildList { - add("-D" + "dokkaCliJarPath=" + dokkaCli.asFile.get().absolutePath) + require(dokkaCli.count() == 1) { + "Expected a single Dokka CLI JAR, but got ${dokkaCli.count()}" + } + add("-D" + "dokkaCliJarPath=" + dokkaCli.singleFile.absolutePath) add("-D" + "dokkaPluginsClasspath=" + dokkaPluginsClasspath.joinToString(";") { it.absolutePath }) } } @@ -69,7 +72,7 @@ testing { testTask.configure { jvmArgumentProviders.add( objects.newInstance().apply { - dokkaCli = configurations.dokkaCliResolver.map { it.singleFile } + dokkaCli.from(configurations.dokkaCliResolver) dokkaPluginsClasspath.from(configurations.dokkaPluginsClasspathResolver) } ) diff --git a/dokka-integration-tests/settings.gradle.kts b/dokka-integration-tests/settings.gradle.kts index 7d824a9c26..212392a4cf 100644 --- a/dokka-integration-tests/settings.gradle.kts +++ b/dokka-integration-tests/settings.gradle.kts @@ -42,7 +42,7 @@ dependencyResolutionManagement { includeBuild("../dokka-runners/runner-gradle-plugin-classic") includeBuild("../dokka-runners/runner-maven-plugin") includeBuild("../dokka-runners/runner-cli") -includeBuild("../.") // depend on the the root project, so integration-tests can depend on the `dokka-subprojects/*` subprojects and their artifacts +includeBuild("../.") // depend on the root project, so integration-tests can depend on projects in `dokka-subprojects/*` include( ":cli", From e8be9da62389139109a6208ee6d293ed4502961c Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:16:15 +0200 Subject: [PATCH 03/31] exclude transitives from dokkaPluginsClasspath --- .../dokkabuild.test-cli-dependencies.gradle.kts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts index dbeb425b0d..76c207efb2 100644 --- a/build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.test-cli-dependencies.gradle.kts @@ -1,6 +1,6 @@ - /* - * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ +/* +* Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +*/ import dokkabuild.utils.declarable import dokkabuild.utils.resolvable import org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE @@ -33,10 +33,13 @@ val dokkaPluginsClasspath: Configuration by configurations.creating { } val dokkaPluginsClasspathResolver: Configuration by configurations.creating { - description = "Resolve Dokka CLI runtime dependencies required to run Dokka CLI, and its plugins." + description = "Resolve Dokka CLI runtime dependencies required to run Dokka CLI, and its plugins. " + + "Transitive dependencies are excluded, and so must be defined explicitly." resolvable() extendsFrom(dokkaPluginsClasspath) attributes { attribute(USAGE_ATTRIBUTE, objects.named(JAVA_RUNTIME)) } + // we don't fetch transitive dependencies here to be able to control external dependencies explicitly + isTransitive = false } From f66c7b10140552dccb4fdf9db0d33254d7538fe0 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:17:07 +0200 Subject: [PATCH 04/31] create a dokka-config.json for each test in the temp directory, don't re-write a shared file in resources --- .../dokka/it/cli/CliIntegrationTest.kt | 114 +++++++++--------- .../integrationTest/resources/my-file.json | 0 2 files changed, 56 insertions(+), 58 deletions(-) delete mode 100644 dokka-integration-tests/cli/src/integrationTest/resources/my-file.json diff --git a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt b/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt index 143a556bdb..9a4caedf91 100644 --- a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt +++ b/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt @@ -278,22 +278,21 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { fun `should accept json as input configuration`() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) - val resourcePath = - javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") - val jsonPath = File(resourcePath) - jsonPath.writeText( - jsonBuilder( - outputPath = dokkaOutputDir.invariantSeparatorsPath, - pluginsClasspath = dokkaPluginsClasspath, - projectPath = File(projectDir, "src").invariantSeparatorsPath, - reportUndocumented = true + val configJson = projectDir.resolve("dokka-config.json").apply { + writeText( + jsonBuilder( + outputPath = dokkaOutputDir.invariantSeparatorsPath, + pluginsClasspath = dokkaPluginsClasspath, + projectPath = File(projectDir, "src").invariantSeparatorsPath, + reportUndocumented = true + ) ) - ) + } val process = ProcessBuilder( "java", "-jar", dokkaCliJarPath, - jsonPath.absolutePath, + configJson.absolutePath, ).redirectErrorStream(true).start() val result = process.awaitProcessResult() @@ -325,47 +324,47 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { fun `global settings should overwrite package options in configuration`() { val dokkaOutputDir = File(projectDir, "output") assertTrue(dokkaOutputDir.mkdirs()) - val resourcePath = - javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") - val jsonPath = File(resourcePath) - jsonPath.writeText( - jsonBuilder( - outputPath = dokkaOutputDir.invariantSeparatorsPath, - pluginsClasspath = dokkaPluginsClasspath, - projectPath = File(projectDir, "src").invariantSeparatorsPath, - globalSourceLinks = """ - { - "localDirectory": "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin", - "remoteUrl": "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin", - "remoteLineSuffix": "#L" - } - """.trimIndent(), - globalExternalDocumentationLinks = """ - { - "url": "https://docs.oracle.com/javase/8/docs/api/", - "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list" - }, - { - "url": "https://kotlinlang.org/api/latest/jvm/stdlib/", - "packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list" - } + + val configJson = projectDir.resolve("dokka-config.json").apply { + writeText( + jsonBuilder( + outputPath = dokkaOutputDir.invariantSeparatorsPath, + pluginsClasspath = dokkaPluginsClasspath, + projectPath = File(projectDir, "src").invariantSeparatorsPath, + globalSourceLinks = """ + { + "localDirectory": "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin", + "remoteUrl": "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin", + "remoteLineSuffix": "#L" + } """.trimIndent(), - globalPerPackageOptions = """ - { - "matchingRegex": ".*", - "skipDeprecated": "true", - "reportUndocumented": "true", - "documentedVisibilities": ["PUBLIC", "PRIVATE", "PROTECTED", "INTERNAL", "PACKAGE"] - } - """.trimIndent(), - reportUndocumented = false - ), - ) + globalExternalDocumentationLinks = """ + { + "url": "https://docs.oracle.com/javase/8/docs/api/", + "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list" + }, + { + "url": "https://kotlinlang.org/api/latest/jvm/stdlib/", + "packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list" + } + """.trimIndent(), + globalPerPackageOptions = """ + { + "matchingRegex": ".*", + "skipDeprecated": "true", + "reportUndocumented": "true", + "documentedVisibilities": ["PUBLIC", "PRIVATE", "PROTECTED", "INTERNAL", "PACKAGE"] + } + """.trimIndent(), + reportUndocumented = false + ) + ) + } val process = ProcessBuilder( "java", "-jar", dokkaCliJarPath, - jsonPath.absolutePath, + configJson.absolutePath, ) .redirectErrorStream(true) .start() @@ -393,24 +392,23 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { @Test fun `relative paths in configuration should work`() { - val resourcePath = - javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!") - val jsonPath = File(resourcePath) - val dokkaOutputDir = File(projectDir, "output-relative") assertTrue(dokkaOutputDir.mkdirs()) - jsonPath.writeText( - jsonBuilder( - outputPath = dokkaOutputDir.invariantSeparatorsPath, - pluginsClasspath = dokkaPluginsClasspath, - projectPath = "src", // relative path + + val configJson = projectDir.resolve("dokka-config.json").apply { + writeText( + jsonBuilder( + outputPath = dokkaOutputDir.invariantSeparatorsPath, + pluginsClasspath = dokkaPluginsClasspath, + projectPath = "src", // relative path + ) ) - ) + } ProcessBuilder( "java", "-jar", dokkaCliJarPath, - jsonPath.absolutePath + configJson.absolutePath ).directory(projectDir).redirectErrorStream(true).start().also { process -> val result = process.awaitProcessResult() assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") diff --git a/dokka-integration-tests/cli/src/integrationTest/resources/my-file.json b/dokka-integration-tests/cli/src/integrationTest/resources/my-file.json deleted file mode 100644 index e69de29bb2..0000000000 From 5b06e076bcd05a9817faee01c793551f7f8ebaaf Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:17:35 +0200 Subject: [PATCH 05/31] add ticket to TODO --- dokka-integration-tests/gradle/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index 2463996ca8..5bc6d696f6 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -271,7 +271,7 @@ val testAllExternalProjects by tasks.registering { val integrationTest by tasks.registering { description = "Lifecycle task for running integration tests" - // TODO - Refactor Maven and CLI integration tests to use Test Suites + // TODO KT-64200 - Refactor Maven and CLI integration tests to use Test Suites // - Reimplement dokkabuild.test-integration.gradle.kts so that `integrationTest` is defined once there dependsOn(tasks.withType()) // all tests in this project are integration tests } From c223939b8179740709d3d8e2457ca7a23a150f4f Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 13:17:38 +0200 Subject: [PATCH 06/31] rm check fix --- build.gradle.kts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 887b0f2a0b..3734b66997 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,7 +90,3 @@ fun includedBuildTasks(taskName: String, filter: (IncludedBuild) -> Boolean = { .filter { it.name != "build-logic" } .filter(filter) .mapNotNull { it.task(":$taskName") } - -tasks.check { - gradle.includedBuilds.forEach { dependsOn(it.task(":check")) } -} From 0a41510913d6d04fad83a26c95efbd7d0cc82c63 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 14:33:55 +0200 Subject: [PATCH 07/31] revert build.gradle.kts --- build.gradle.kts | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3734b66997..2a6ac68dc2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,43 +11,35 @@ val gradlePluginIncludedBuilds = listOf("runner-gradle-plugin-classic") addDependencyOnSameTasksOfIncludedBuilds("assemble", "build", "clean", "check") -registerParentGroupTasks( - "publishing", taskNames = listOf( - "publishAllPublicationsToMavenCentralRepository", - "publishAllPublicationsToProjectLocalRepository", - "publishAllPublicationsToSnapshotRepository", - "publishAllPublicationsToSpaceDevRepository", - "publishAllPublicationsToSpaceTestRepository", - "publishToMavenLocal" - ) -) { +registerParentGroupTasks("publishing", taskNames = listOf( + "publishAllPublicationsToMavenCentralRepository", + "publishAllPublicationsToProjectLocalRepository", + "publishAllPublicationsToSnapshotRepository", + "publishAllPublicationsToSpaceDevRepository", + "publishAllPublicationsToSpaceTestRepository", + "publishToMavenLocal" +)) { it.name in publishedIncludedBuilds } -registerParentGroupTasks( - "gradle plugin", taskNames = listOf( - "publishPlugins", - "validatePlugins" - ) -) { +registerParentGroupTasks("gradle plugin", taskNames = listOf( + "publishPlugins", + "validatePlugins" +)) { it.name in gradlePluginIncludedBuilds } -registerParentGroupTasks( - "bcv", taskNames = listOf( - "apiDump", - "apiCheck", - "apiBuild" - ) -) { +registerParentGroupTasks("bcv", taskNames = listOf( + "apiDump", + "apiCheck", + "apiBuild" +)) { it.name in publishedIncludedBuilds } -registerParentGroupTasks( - "verification", taskNames = listOf( - "test" - ) -) +registerParentGroupTasks("verification", taskNames = listOf( + "test" +)) tasks.register("integrationTest") { group = "verification" From 16278e050cd753b2f395aa41ec20e547f8ff7dd8 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:21:36 +0200 Subject: [PATCH 08/31] Update Maven Integration tests to use JVM test suites Part of KT-64200 WIP --- .../dokkabuild.test-integration.gradle.kts | 144 ++++++------- build.gradle.kts | 5 + .../gradle/build.gradle.kts | 1 - .../maven/build.gradle.kts | 203 ++++++++++++++++-- .../it/maven => main/kotlin}/settingsXml.kt | 2 +- .../kotlin}/BiojavaIntegrationTest.kt | 2 +- .../kotlin}/MavenIntegrationTest.kt | 3 +- 7 files changed, 268 insertions(+), 92 deletions(-) rename dokka-integration-tests/maven/src/{integrationTest/kotlin/org/jetbrains/dokka/it/maven => main/kotlin}/settingsXml.kt (97%) rename dokka-integration-tests/maven/src/{integrationTest/kotlin/org/jetbrains/dokka/it/maven => testExternalProjectBioJava/kotlin}/BiojavaIntegrationTest.kt (96%) rename dokka-integration-tests/maven/src/{integrationTest/kotlin/org/jetbrains/dokka/it/maven => testTemplateProjectMaven/kotlin}/MavenIntegrationTest.kt (98%) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts index 3a33ffc5a8..6d1dfd3cb9 100644 --- a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts @@ -1,72 +1,72 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. - */ - -import org.gradle.api.tasks.testing.logging.TestExceptionFormat -import org.gradle.api.tasks.testing.logging.TestLogEvent - -plugins { - id("dokkabuild.kotlin-jvm") -} - -val integrationTestSourceSet: SourceSet = sourceSets.create("integrationTest") { - compileClasspath += sourceSets.main.get().output - runtimeClasspath += sourceSets.main.get().output -} - -val integrationTestImplementation: Configuration by configurations.getting { - extendsFrom(configurations.implementation.get()) -} - -val integrationTestRuntimeOnly: Configuration by configurations.getting { - extendsFrom(configurations.runtimeOnly.get()) -} - -/** - * Dokka's integration test task is not cacheable because the HTML outputs - * it produces when running the tests are used for showcasing resulting documentation, - * which does not work well with caching. - * - * At the moment there are two problems that do not allow to make it cacheable: - * - * 1. The task's inputs are such that changes in Dokka's code do not invalidate the cache, - * because it is run with the same version of Dokka (`"DOKKA_VERSION"`) on the same - * test project inputs. - * 2. The tests generate HTML output which is then used to showcase documentation. - * The outputs are usually copied to a location from which it will be served. - * However, if the test is cacheable, it produces no outputs, so no documentation - * to showcase. It needs to be broken into two separate tasks: one cacheable for running - * the tests and producing HTML output, and another non-cacheable for copying the output. - * - * @see [org.jetbrains.dokka.it.TestOutputCopier] for more details on showcasing documentation - */ -@DisableCachingByDefault(because = "Contains incorrect inputs/outputs configuration, see the KDoc for details") -abstract class NonCacheableIntegrationTest : Test() - -val integrationTest by tasks.registering(NonCacheableIntegrationTest::class) { - maxHeapSize = "2G" - description = "Runs integration tests." - group = "verification" - testClassesDirs = integrationTestSourceSet.output.classesDirs - classpath = integrationTestSourceSet.runtimeClasspath - - useJUnitPlatform { - if (dokkaBuild.integrationTestUseK2.get()) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") - } - - systemProperty("org.jetbrains.dokka.experimental.tryK2", dokkaBuild.integrationTestUseK2.get()) - - dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> - maxParallelForks = parallelism - } - - environment("isExhaustive", dokkaBuild.integrationTestExhaustive.get()) - - testLogging { - exceptionFormat = TestExceptionFormat.FULL - events(TestLogEvent.SKIPPED, TestLogEvent.FAILED) - showExceptions = true - showCauses = true - showStackTraces = true - } -} +///* +// * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. +// */ +// +//import org.gradle.api.tasks.testing.logging.TestExceptionFormat +//import org.gradle.api.tasks.testing.logging.TestLogEvent +// +//plugins { +// id("dokkabuild.kotlin-jvm") +//} +// +//val integrationTestSourceSet: SourceSet = sourceSets.create("integrationTest") { +// compileClasspath += sourceSets.main.get().output +// runtimeClasspath += sourceSets.main.get().output +//} +// +//val integrationTestImplementation: Configuration by configurations.getting { +// extendsFrom(configurations.implementation.get()) +//} +// +//val integrationTestRuntimeOnly: Configuration by configurations.getting { +// extendsFrom(configurations.runtimeOnly.get()) +//} +// +///** +// * Dokka's integration test task is not cacheable because the HTML outputs +// * it produces when running the tests are used for showcasing resulting documentation, +// * which does not work well with caching. +// * +// * At the moment there are two problems that do not allow to make it cacheable: +// * +// * 1. The task's inputs are such that changes in Dokka's code do not invalidate the cache, +// * because it is run with the same version of Dokka (`"DOKKA_VERSION"`) on the same +// * test project inputs. +// * 2. The tests generate HTML output which is then used to showcase documentation. +// * The outputs are usually copied to a location from which it will be served. +// * However, if the test is cacheable, it produces no outputs, so no documentation +// * to showcase. It needs to be broken into two separate tasks: one cacheable for running +// * the tests and producing HTML output, and another non-cacheable for copying the output. +// * +// * @see [org.jetbrains.dokka.it.TestOutputCopier] for more details on showcasing documentation +// */ +//@DisableCachingByDefault(because = "Contains incorrect inputs/outputs configuration, see the KDoc for details") +//abstract class NonCacheableIntegrationTest : Test() +// +//val integrationTest by tasks.registering(NonCacheableIntegrationTest::class) { +// maxHeapSize = "2G" +// description = "Runs integration tests." +// group = "verification" +// testClassesDirs = integrationTestSourceSet.output.classesDirs +// classpath = integrationTestSourceSet.runtimeClasspath +// +// useJUnitPlatform { +// if (dokkaBuild.integrationTestUseK2.get()) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") +// } +// +// systemProperty("org.jetbrains.dokka.experimental.tryK2", dokkaBuild.integrationTestUseK2.get()) +// +// dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> +// maxParallelForks = parallelism +// } +// +// environment("isExhaustive", dokkaBuild.integrationTestExhaustive.get()) +// +// testLogging { +// exceptionFormat = TestExceptionFormat.FULL +// events(TestLogEvent.SKIPPED, TestLogEvent.FAILED) +// showExceptions = true +// showCauses = true +// showStackTraces = true +// } +//} diff --git a/build.gradle.kts b/build.gradle.kts index 2a6ac68dc2..c38244f3d7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -82,3 +82,8 @@ fun includedBuildTasks(taskName: String, filter: (IncludedBuild) -> Boolean = { .filter { it.name != "build-logic" } .filter(filter) .mapNotNull { it.task(":$taskName") } + + +tasks.check { + gradle.includedBuilds.forEach { dependsOn(it.task(":check")) } +} diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index 5bc6d696f6..463ee988e7 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -15,7 +15,6 @@ plugins { id("dokkabuild.kotlin-jvm") id("dokkabuild.dev-maven-publish") `jvm-test-suite` - `java-test-fixtures` } dependencies { diff --git a/dokka-integration-tests/maven/build.gradle.kts b/dokka-integration-tests/maven/build.gradle.kts index 4b2977b491..f92ced62df 100644 --- a/dokka-integration-tests/maven/build.gradle.kts +++ b/dokka-integration-tests/maven/build.gradle.kts @@ -1,20 +1,27 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +@file:Suppress("UnstableApiUsage") + import dokkabuild.tasks.GitCheckoutTask import org.gradle.api.tasks.PathSensitivity.RELATIVE +import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL +import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED +import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED +import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP plugins { - id("dokkabuild.test-integration") - id("dokkabuild.setup-maven-cli") + id("dokkabuild.kotlin-jvm") id("dokkabuild.dev-maven-publish") + id("dokkabuild.setup-maven-cli") + `jvm-test-suite` } dependencies { - implementation(projects.utilities) + api(projects.utilities) - implementation(kotlin("test-junit5")) - implementation(libs.junit.jupiterApi) + api(kotlin("test-junit5")) + api(libs.junit.jupiterApi) val dokkaVersion = project.version.toString() // We're using Gradle included-builds and dependency substitution, so we @@ -43,26 +50,190 @@ dependencies { val templateProjectsDir = layout.projectDirectory.dir("projects") -val dokkaSubprojects = gradle.includedBuild("dokka") -val mavenPlugin = gradle.includedBuild("runner-maven-plugin") -tasks.integrationTest { - dependsOn(checkoutBioJava) +/** + * Provide files required for running Dokka CLI in a build cache friendly way. + */ +abstract class MvnBinaryPathArgProvider : CommandLineArgumentProvider { + @get:Classpath + abstract val maven: RegularFileProperty - dependsOn(tasks.installMavenBinary) - val mvn = mavenCliSetup.mvn - inputs.file(mvn) + override fun asArguments(): Iterable = buildList { + add("-D" + "mavenBinaryFile=" + maven.asFile.get().absolutePath) + } +} + + +tasks.withType().configureEach { + setForkEvery(1) + maxHeapSize = "2G" + dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> + maxParallelForks = parallelism + } + + val useK2 = dokkaBuild.integrationTestUseK2.get() + + useJUnitPlatform { + if (useK2) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") + } + + systemProperty("org.jetbrains.dokka.experimental.tryK2", useK2) + // allow inspecting projects in temporary dirs after a test fails + systemProperty( + "junit.jupiter.tempdir.cleanup.mode.default", + if (dokkaBuild.isCI.get()) "ALWAYS" else "ON_SUCCESS", + ) val dokkaVersion = provider { project.version.toString() } inputs.property("dokkaVersion", dokkaVersion) - - doFirst("workaround for https://github.com/gradle/gradle/issues/24267") { + doFirst("set DOKKA_VERSION environment variable (workaround for https://github.com/gradle/gradle/issues/24267)") { environment("DOKKA_VERSION", dokkaVersion.get()) - environment("MVN_BINARY_PATH", mvn.get().asFile.invariantSeparatorsPath) } - devMavenPublish.configureTask(this) + // environment() isn't Provider API compatible yet https://github.com/gradle/gradle/issues/11534 + dokkaBuild.integrationTestExhaustive.orNull?.let { exhaustive -> + environment("isExhaustive", exhaustive) + } + + dependsOn(tasks.installMavenBinary) + jvmArgumentProviders.add( + objects.newInstance().apply { + maven = mavenCliSetup.mvn + } + ) + + testLogging { + exceptionFormat = FULL + events(SKIPPED, FAILED) + showExceptions = true + showCauses = true + showStackTraces = true + } + + // The tests produce report data and generated Dokka output. + // Always cache them so Gradle can skip running integration tests if nothing has changed. + outputs.cacheIf("always cache") { true } +} + +testing { + suites { + withType().configureEach { + useJUnitJupiter() + + dependencies { + // test suites are independent by default (unlike the test source set), and must manually depend on the project + implementation(project()) + } + + targets.configureEach { + testTask.configure { + doFirst { + logger.info("running $path with javaLauncher:${javaLauncher.orNull?.metadata?.javaRuntimeVersion}") + } + } + } + } + + registerTestProjectSuite("testTemplateProjectMaven", "it-maven") + registerTestProjectSuite("testExternalProjectBioJava", "biojava/biojava") { + targets.configureEach { + testTask.configure { + dependsOn(checkoutBioJava) + // register the whole directory as an input because it contains the git diff + inputs + .dir(templateProjectsDir.file("biojava")) + .withPropertyName("biojavaProjectDir") + } + } + } + } +} + +tasks.check { + dependsOn(testing.suites) +} + +/** + * Create a new [JvmTestSuite] for a Gradle project. + * + * @param[projectPath] path to the Gradle project that will be tested by this suite, relative to [templateProjectsDir]. + * The directory will be passed as a system property, `templateProjectDir`. + */ +fun TestingExtension.registerTestProjectSuite( + name: String, + projectPath: String, + jvm: JavaLanguageVersion? = null, + configure: JvmTestSuite.() -> Unit = {}, +) { + val templateProjectDir = templateProjectsDir.dir(projectPath) + + suites.register(name) { + targets.configureEach { + testTask.configure { + // Register the project dir as a specific input, so changes in other projects don't affect the caching of this test + inputs.dir(templateProjectDir) + .withPropertyName("templateProjectDir") + .withPathSensitivity(RELATIVE) + + // Pass the template dir in as a property, it is accessible in tests. + systemProperty("templateProjectDir", templateProjectDir.asFile.invariantSeparatorsPath) + + devMavenPublish.configureTask(this) + + if (jvm != null) { + javaLauncher = javaToolchains.launcherFor { languageVersion = jvm } + } + + // For validation, on CI the generated output is uploaded, so the test must produce output in + // DOKKA_TEST_OUTPUT_PATH. For Gradle up-to-date checks the output dir must be specified. + val testOutputPath = System.getenv("DOKKA_TEST_OUTPUT_PATH") + inputs.property("testOutputPath", testOutputPath).optional(true) + if (testOutputPath != null) { + outputs.dir(testOutputPath).withPropertyName("testOutput") + } + } + } + configure() + } +} + +//region project tests management + +// set up task ordering - template projects (which are generally faster) should be tested before external projects +val testTemplateProjectsTasks = tasks.withType().matching { it.name.startsWith("testTemplateProject") } +val testExternalProjectsTasks = tasks.withType().matching { it.name.startsWith("testExternalProject") } + +testTemplateProjectsTasks.configureEach { + shouldRunAfter(tasks.test) +} +testExternalProjectsTasks.configureEach { + shouldRunAfter(tasks.test) + shouldRunAfter(testTemplateProjectsTasks) +} + +// define lifecycle tasks for project tests +val testAllTemplateProjects by tasks.registering { + description = "Lifecycle task for running all template-project tests" + group = VERIFICATION_GROUP + dependsOn(testTemplateProjectsTasks) + doNotTrackState("lifecycle task, should always run") +} + +val testAllExternalProjects by tasks.registering { + description = "Lifecycle task for running all external-project tests" + group = VERIFICATION_GROUP + shouldRunAfter(testAllTemplateProjects) + dependsOn(testExternalProjectsTasks) + doNotTrackState("lifecycle task, should always run") +} + +val integrationTest by tasks.registering { + description = "Lifecycle task for running integration tests" + // TODO - Refactor Maven and CLI integration tests to use Test Suites + // - Reimplement dokkabuild.test-integration.gradle.kts so that `integrationTest` is defined once there + dependsOn(tasks.withType()) // all tests in this project are integration tests } +//endregion val checkoutBioJava by tasks.registering(GitCheckoutTask::class) { uri = "https://github.com/biojava/biojava.git" diff --git a/dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/settingsXml.kt b/dokka-integration-tests/maven/src/main/kotlin/settingsXml.kt similarity index 97% rename from dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/settingsXml.kt rename to dokka-integration-tests/maven/src/main/kotlin/settingsXml.kt index 78599db461..55bac78adc 100644 --- a/dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/settingsXml.kt +++ b/dokka-integration-tests/maven/src/main/kotlin/settingsXml.kt @@ -12,7 +12,7 @@ import java.nio.file.Paths /** Create `settings.xml` file contents, with the custom dev Maven repos. */ @Language("xml") -fun createSettingsXml(): String { +public fun createSettingsXml(): String { /** file-based Maven repositories with Dokka dependencies */ val devMavenRepositories: List by systemProperty { repos -> repos.split(",").map { Paths.get(it) } diff --git a/dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/BiojavaIntegrationTest.kt b/dokka-integration-tests/maven/src/testExternalProjectBioJava/kotlin/BiojavaIntegrationTest.kt similarity index 96% rename from dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/BiojavaIntegrationTest.kt rename to dokka-integration-tests/maven/src/testExternalProjectBioJava/kotlin/BiojavaIntegrationTest.kt index d029a488c1..48d3d90b87 100644 --- a/dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/BiojavaIntegrationTest.kt +++ b/dokka-integration-tests/maven/src/testExternalProjectBioJava/kotlin/BiojavaIntegrationTest.kt @@ -14,7 +14,7 @@ import kotlin.test.assertTrue class BiojavaIntegrationTest : AbstractIntegrationTest(), TestOutputCopier { private val currentDokkaVersion: String = checkNotNull(System.getenv("DOKKA_VERSION")) - private val mavenBinaryFile: File = File(checkNotNull(System.getenv("MVN_BINARY_PATH"))) + private val mavenBinaryFile: File by systemProperty { File(it) } override val projectOutputLocation: File by lazy { File(projectDir, "biojava-core/target/dokkaJavadoc") } private val localSettingsXml: File by lazy { diff --git a/dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt b/dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt similarity index 98% rename from dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt rename to dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt index f193d669b3..8795153e1b 100644 --- a/dokka-integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt +++ b/dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt @@ -7,6 +7,7 @@ package org.jetbrains.dokka.it.maven import org.jetbrains.dokka.it.AbstractIntegrationTest import org.jetbrains.dokka.it.ProcessResult import org.jetbrains.dokka.it.awaitProcessResult +import org.jetbrains.dokka.it.systemProperty import java.io.File import kotlin.test.* @@ -14,7 +15,7 @@ class MavenIntegrationTest : AbstractIntegrationTest() { private val currentDokkaVersion: String = checkNotNull(System.getenv("DOKKA_VERSION")) - private val mavenBinaryFile: File = File(checkNotNull(System.getenv("MVN_BINARY_PATH"))) + private val mavenBinaryFile: File by systemProperty { File(it) } private val localSettingsXml: File by lazy { projectDir.resolve("local-settings.xml").apply { From 38f9a5900f357e4217f45345a2d146daadd06151 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 15:15:12 +0200 Subject: [PATCH 09/31] re-add `required dependencies of plugin-base` comment --- dokka-integration-tests/cli/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 625633dbb3..11375f60c4 100644 --- a/dokka-integration-tests/cli/build.gradle.kts +++ b/dokka-integration-tests/cli/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { dokkaCli("org.jetbrains.dokka:runner-cli") + //region required dependencies of plugin-base dokkaPluginsClasspath("org.jetbrains.dokka:plugin-base") dokkaPluginsClasspath(libs.kotlinx.html) dokkaPluginsClasspath(libs.freemarker) @@ -35,6 +36,7 @@ dependencies { attribute(BUNDLING_ATTRIBUTE, project.objects.named(SHADOWED)) } } + //endregion } /** From 504c87cf3a9a6febc58199fa03926ef39959724d Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 17:43:25 +0200 Subject: [PATCH 10/31] incrementally print stdout from Process --- .../main/kotlin/org/jetbrains/dokka/it/processUtils.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt index 9ae4036f69..8ad8ec08e7 100644 --- a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt +++ b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt @@ -1,6 +1,7 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ + package org.jetbrains.dokka.it import java.util.concurrent.TimeUnit @@ -11,10 +12,11 @@ class ProcessResult( ) fun Process.awaitProcessResult(): ProcessResult { - waitFor(60, TimeUnit.SECONDS) + val output = inputStream.bufferedReader().lineSequence() + .onEach { println(it) } + .joinToString("\n") - val output = inputStream.reader().readText() - println(output) + waitFor(60, TimeUnit.SECONDS) return ProcessResult( exitCode = exitValue(), From ca6034b6a381b47b452898b9917101be234752f1 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 24 Apr 2024 18:09:25 +0200 Subject: [PATCH 11/31] rm unnecessary coroutines dependency --- dokka-integration-tests/utilities/build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/dokka-integration-tests/utilities/build.gradle.kts b/dokka-integration-tests/utilities/build.gradle.kts index 8cb8d908e2..1787975cd0 100644 --- a/dokka-integration-tests/utilities/build.gradle.kts +++ b/dokka-integration-tests/utilities/build.gradle.kts @@ -13,7 +13,6 @@ dependencies { // thus these dependencies are needed. Ideally, they should be removed. implementation(kotlin("test-junit5")) - implementation(libs.kotlinx.coroutines.core) implementation(libs.jsoup) implementation(libs.eclipse.jgit) } From fac9cb20ae6680c597ab0093dd82c84c02ed8a85 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 29 May 2024 12:54:13 +0200 Subject: [PATCH 12/31] update BioJava test task --- .github/workflows/preview-publish-web-s3.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview-publish-web-s3.yml b/.github/workflows/preview-publish-web-s3.yml index 93d7269df5..89a581a7d9 100644 --- a/.github/workflows/preview-publish-web-s3.yml +++ b/.github/workflows/preview-publish-web-s3.yml @@ -106,7 +106,7 @@ jobs: with: gradle-home-cache-cleanup: true - name: Document biojava-core - run: ./gradlew :dokka-integration-tests:maven:integrationTest --tests org.jetbrains.dokka.it.maven.BiojavaIntegrationTest --stacktrace + run: ./gradlew :dokka-integration-tests:maven:testExternalProjectBioJava --stacktrace env: DOKKA_TEST_OUTPUT_PATH: /home/runner/work/dokka/biojava - name: Configure AWS credentials for S3 access From c2302960e4291ccea3a2e39e818e853a21bc6a5c Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:35:31 +0200 Subject: [PATCH 13/31] refactor integration test config - Re-introduce shared integration-test convention plugin, to de-duplicate common IT config. - Add util for adding system properties & registering the values as appropriate Gradle task inputs. (This required replacing env-vars with system props.) --- .../dokkabuild.test-integration.gradle.kts | 165 +++++++------ .../dokkabuild/DevMavenPublishExtension.kt | 44 ++-- .../dokkabuild/utils/SystemPropertyAdder.kt | 134 +++++++++++ dokka-integration-tests/cli/build.gradle.kts | 53 ++--- .../dokka/it/cli/CliIntegrationTest.kt | 0 .../it/cli/AbstractCliIntegrationTest.kt | 5 +- .../gradle/build.gradle.kts | 220 ++++++------------ .../build.gradle.kts | 4 +- .../gradle/AbstractGradleIntegrationTest.kt | 9 +- .../dokka/it/gradle/TestEnvironment.kt | 5 +- .../kotlin/Android0GradleIntegrationTest.kt | 37 ++- .../SequentialTasksExecutionStressTest.kt | 2 +- .../maven/build.gradle.kts | 145 +++--------- .../kotlin/BiojavaIntegrationTest.kt | 4 +- .../kotlin/MavenIntegrationTest.kt | 4 +- .../jetbrains/dokka/it/systemProperties.kt | 21 ++ settings.gradle.kts | 2 +- 17 files changed, 432 insertions(+), 422 deletions(-) create mode 100644 build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt rename dokka-integration-tests/cli/src/{integrationTest => cliIntegrationTest}/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt (100%) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts index 6d1dfd3cb9..7ce21aca0a 100644 --- a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts @@ -1,72 +1,93 @@ -///* -// * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. -// */ -// -//import org.gradle.api.tasks.testing.logging.TestExceptionFormat -//import org.gradle.api.tasks.testing.logging.TestLogEvent -// -//plugins { -// id("dokkabuild.kotlin-jvm") -//} -// -//val integrationTestSourceSet: SourceSet = sourceSets.create("integrationTest") { -// compileClasspath += sourceSets.main.get().output -// runtimeClasspath += sourceSets.main.get().output -//} -// -//val integrationTestImplementation: Configuration by configurations.getting { -// extendsFrom(configurations.implementation.get()) -//} -// -//val integrationTestRuntimeOnly: Configuration by configurations.getting { -// extendsFrom(configurations.runtimeOnly.get()) -//} -// -///** -// * Dokka's integration test task is not cacheable because the HTML outputs -// * it produces when running the tests are used for showcasing resulting documentation, -// * which does not work well with caching. -// * -// * At the moment there are two problems that do not allow to make it cacheable: -// * -// * 1. The task's inputs are such that changes in Dokka's code do not invalidate the cache, -// * because it is run with the same version of Dokka (`"DOKKA_VERSION"`) on the same -// * test project inputs. -// * 2. The tests generate HTML output which is then used to showcase documentation. -// * The outputs are usually copied to a location from which it will be served. -// * However, if the test is cacheable, it produces no outputs, so no documentation -// * to showcase. It needs to be broken into two separate tasks: one cacheable for running -// * the tests and producing HTML output, and another non-cacheable for copying the output. -// * -// * @see [org.jetbrains.dokka.it.TestOutputCopier] for more details on showcasing documentation -// */ -//@DisableCachingByDefault(because = "Contains incorrect inputs/outputs configuration, see the KDoc for details") -//abstract class NonCacheableIntegrationTest : Test() -// -//val integrationTest by tasks.registering(NonCacheableIntegrationTest::class) { -// maxHeapSize = "2G" -// description = "Runs integration tests." -// group = "verification" -// testClassesDirs = integrationTestSourceSet.output.classesDirs -// classpath = integrationTestSourceSet.runtimeClasspath -// -// useJUnitPlatform { -// if (dokkaBuild.integrationTestUseK2.get()) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") -// } -// -// systemProperty("org.jetbrains.dokka.experimental.tryK2", dokkaBuild.integrationTestUseK2.get()) -// -// dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> -// maxParallelForks = parallelism -// } -// -// environment("isExhaustive", dokkaBuild.integrationTestExhaustive.get()) -// -// testLogging { -// exceptionFormat = TestExceptionFormat.FULL -// events(TestLogEvent.SKIPPED, TestLogEvent.FAILED) -// showExceptions = true -// showCauses = true -// showStackTraces = true -// } -//} +/* + * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +@file:Suppress("UnstableApiUsage") + +import dokkabuild.utils.systemProperty +import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL +import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED +import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED + +plugins { + id("dokkabuild.base") + `jvm-test-suite` +} + +val integrationTest by tasks.registering { + description = "Lifecycle task for running all integration tests." + group = LifecycleBasePlugin.VERIFICATION_GROUP +} + +tasks.withType().configureEach { + setForkEvery(1) + maxHeapSize = "2G" + dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> + maxParallelForks = parallelism + } + + systemProperty.inputProperty("dokkaVersion", provider { project.version.toString() }) + systemProperty.inputProperty("dokkaVersionOverride", dokkaBuild.integrationTestDokkaVersionOverride) + .optional(true) + + val useK2 = dokkaBuild.integrationTestUseK2 + systemProperty.inputProperty("org.jetbrains.dokka.experimental.tryK2", useK2) + .optional(true) + + useJUnitPlatform { + if (useK2.get()) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") + } + systemProperty.inputProperty("isExhaustive", dokkaBuild.integrationTestExhaustive) + + // allow inspecting projects in temporary dirs after a test fails + systemProperty.inputProperty( + "junit.jupiter.tempdir.cleanup.mode.default", + dokkaBuild.isCI.map { if (it) "ALWAYS" else "ON_SUCCESS" } + ) + + inputs.property("ENABLE_DEBUG", providers.systemProperty("ENABLE_DEBUG")) + .optional(true) + + testLogging { + exceptionFormat = FULL + events(SKIPPED, FAILED) + showExceptions = true + showCauses = true + showStackTraces = true + } + + // For validation, on CI the generated output is uploaded, so the test must produce output in + // DOKKA_TEST_OUTPUT_PATH. For Gradle up-to-date checks the output dir must be specified. + val testOutputPath = System.getenv("DOKKA_TEST_OUTPUT_PATH") + inputs.property("testOutputPath", testOutputPath).optional(true) + if (testOutputPath != null) { + outputs.dir(testOutputPath).withPropertyName("testOutput") + } + + // The tests produce report data and generated Dokka output. + // Always cache them so Gradle can skip running integration tests if nothing has changed. + outputs.cacheIf("always cache") { true } +} + +testing { + suites { + withType().configureEach { + useJUnitJupiter() + + targets.configureEach { + testTask.configure { + doFirst { + logger.info("running $path with javaLauncher:${javaLauncher.orNull?.metadata?.javaRuntimeVersion}") + } + } + } + } + } +} + +integrationTest.configure { + dependsOn(testing.suites) +} + +tasks.check { + dependsOn(integrationTest) +} diff --git a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt index 1bc681c219..8076318c53 100644 --- a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt +++ b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt @@ -3,9 +3,11 @@ */ package dokkabuild +import dokkabuild.utils.systemProperty import org.gradle.api.Task import org.gradle.api.file.FileCollection -import org.gradle.api.file.FileTree +import org.gradle.api.file.FileSystemLocation +import org.gradle.api.provider.Provider import org.gradle.api.tasks.PathSensitivity.RELATIVE import org.gradle.process.JavaForkOptions @@ -21,35 +23,41 @@ abstract class DevMavenPublishExtension( /** * Files suitable for registering as a task input (as in, the files are reproducible-build compatible). */ - private val devMavenRepositoriesInputFiles: FileTree = devMavenRepositories - .asFileTree - .matching { - // Exclude Maven Metadata files because they contain timestamps, meaning tasks that use - // devMavenRepositories as an input will never be up-to-date. - // The Gradle Module Metadata contains the same information (and more), - // so the Maven metadata is redundant. - exclude("**/maven-metadata*.xml") - } + private val devMavenRepositoriesInputFiles: Provider> = + devMavenRepositories + .elements + .map { files -> + files + .filterNot { + // Exclude Maven Metadata files because they contain timestamps, meaning tasks that use + // devMavenRepositories as an input will never be up-to-date. + // The Gradle Module Metadata contains the same information (and more), + // so the Maven metadata is redundant. + it.asFile.name.run { startsWith("maven-metadata") && endsWith(".xml") } + } + // sort the elements to ensure that the contents are reproducible + .toSortedSet { a, b -> a.asFile.compareTo(b.asFile) } + } /** * Configures [task] to register [devMavenRepositories] as a task input, - * and (if possible) adds `devMavenRepository` as a [JavaForkOptions.systemProperty]. + * and (if possible) adds `devMavenRepositories` as a [JavaForkOptions.systemProperty]. */ fun configureTask(task: Task) { task.inputs.files(devMavenRepositoriesInputFiles) .withPropertyName("devMavenPublish.devMavenRepositoriesInputFiles") .withPathSensitivity(RELATIVE) + .ignoreEmptyDirectories() task.dependsOn(devMavenRepositories) if (task is JavaForkOptions) { - task.doFirst("devMavenRepositories systemProperty") { - // workaround https://github.com/gradle/gradle/issues/24267 - task.systemProperty( - "devMavenRepositories", - devMavenRepositories.joinToString(",") { it.canonicalFile.invariantSeparatorsPath } - ) - } + task.jvmArgumentProviders.systemProperty( + "devMavenRepositories", + devMavenRepositoriesInputFiles.map { paths -> + paths.joinToString(",") { it.asFile.invariantSeparatorsPath } + }, + ) } } diff --git a/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt new file mode 100644 index 0000000000..2399d25f46 --- /dev/null +++ b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ +package dokkabuild.utils + +import org.gradle.api.file.Directory +import org.gradle.api.file.FileCollection +import org.gradle.api.file.RegularFile +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskInputFilePropertyBuilder +import org.gradle.api.tasks.TaskInputPropertyBuilder +import org.gradle.api.tasks.testing.Test +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.findByType +import org.gradle.process.CommandLineArgumentProvider +import java.io.File +import javax.inject.Inject + + +/** + * Utility for adding a System Property command line arguments to this [Test] task, + * and correctly registering the values as task inputs (for Gradle up-to-date checks). + */ +// https://github.com/gradle/gradle/issues/11534 +// https://github.com/gradle/gradle/issues/12247 +val Test.systemProperty: SystemPropertyAdder + get() { + val spa = extensions.findByType() + ?: extensions.create("SystemPropertyAdder", this) + return spa + } + + +abstract class SystemPropertyAdder @Inject internal constructor( + private val task: Test, +) { + fun inputDirectory( + key: String, + value: Directory, + ): TaskInputFilePropertyBuilder { + task.jvmArgumentProviders.add( + SystemPropertyArgumentProvider(key, value) { + it.asFile.invariantSeparatorsPath + } + ) + return task.inputs.dir(value) + .withPropertyName("SystemProperty input directory $key") + } + + fun inputFile( + key: String, + file: RegularFile, + ): TaskInputFilePropertyBuilder { + task.jvmArgumentProviders.add( + SystemPropertyArgumentProvider(key, file) { + it.asFile.invariantSeparatorsPath + } + ) + return task.inputs.file(file) + .withPropertyName("SystemProperty input file $key") + } + + fun inputFile( + key: String, + file: Provider, + ): TaskInputFilePropertyBuilder { + task.jvmArgumentProviders.add( + SystemPropertyArgumentProvider(key, file) { + it.orNull?.invariantSeparatorsPath + } + ) + return task.inputs.file(file) + .withPropertyName("SystemProperty input file $key") + } + + @JvmName("inputRegularFile") + fun inputFile( + key: String, + file: Provider, + ): TaskInputFilePropertyBuilder = inputFile(key, file.map { it.asFile }) + + fun inputFiles( + key: String, + files: Provider, + ): TaskInputFilePropertyBuilder { + task.jvmArgumentProviders.add( + SystemPropertyArgumentProvider(key, files) { it.orNull?.asPath } + ) + return task.inputs.files(files) + .withPropertyName("SystemProperty input files $key") + } + + fun inputProperty( + key: String, + value: Provider, + ): TaskInputPropertyBuilder { + task.jvmArgumentProviders.add( + SystemPropertyArgumentProvider(key, value) { it.orNull } + ) + return task.inputs.property("SystemProperty input property $key", value) + } + + @JvmName("inputBooleanProperty") + fun inputProperty( + key: String, + value: Provider, + ): TaskInputPropertyBuilder = inputProperty(key, value.map { it.toString() }) +} + +private class SystemPropertyArgumentProvider( + @get:Input + val key: String, + private val value: T, + private val transformer: (value: T) -> String?, +) : CommandLineArgumentProvider { + override fun asArguments(): Iterable { + val value = transformer(value) ?: return emptyList() + return listOf("-D$key=$value") + } +} + +fun MutableList.systemProperty( + key: String, + value: Provider, +) { + add( + SystemPropertyArgumentProvider( + key = key, + value = value, + transformer = { it.orNull }, + ) + ) +} diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 11375f60c4..64a5c942d5 100644 --- a/dokka-integration-tests/cli/build.gradle.kts +++ b/dokka-integration-tests/cli/build.gradle.kts @@ -3,13 +3,14 @@ */ @file:Suppress("UnstableApiUsage") +import dokkabuild.utils.systemProperty import org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE import org.gradle.api.attributes.Bundling.SHADOWED plugins { id("dokkabuild.kotlin-jvm") + id("dokkabuild.test-integration") id("dokkabuild.test-cli-dependencies") - `jvm-test-suite` } dependencies { @@ -39,51 +40,35 @@ dependencies { //endregion } -/** - * Provide files required for running Dokka CLI in a build cache friendly way. - */ -abstract class DokkaCliClasspathProvider : CommandLineArgumentProvider { - @get:Classpath - abstract val dokkaCli: ConfigurableFileCollection - - @get:Classpath - abstract val dokkaPluginsClasspath: ConfigurableFileCollection - - override fun asArguments(): Iterable = buildList { - require(dokkaCli.count() == 1) { - "Expected a single Dokka CLI JAR, but got ${dokkaCli.count()}" - } - add("-D" + "dokkaCliJarPath=" + dokkaCli.singleFile.absolutePath) - add("-D" + "dokkaPluginsClasspath=" + dokkaPluginsClasspath.joinToString(";") { it.absolutePath }) - } -} - - testing { suites { withType().configureEach { - useJUnitJupiter() - } - - register("integrationTest") { dependencies { implementation(project()) } + } + + register("cliIntegrationTest") { targets.configureEach { testTask.configure { - jvmArgumentProviders.add( - objects.newInstance().apply { - dokkaCli.from(configurations.dokkaCliResolver) - dokkaPluginsClasspath.from(configurations.dokkaPluginsClasspathResolver) + + val dokkaCliJar = configurations.dokkaCliResolver + .map { files -> + requireNotNull(files.singleOrNull()) { + "Expected a single Dokka CLI JAR, but got ${files.count()}" + } } - ) + + systemProperty + .inputFile("dokkaCliJarPath", dokkaCliJar) + .withNormalizer(ClasspathNormalizer::class) + + systemProperty + .inputFiles("dokkaPluginsClasspath", configurations.dokkaPluginsClasspathResolver) + .withNormalizer(ClasspathNormalizer::class) } } } } } - -tasks.check { - dependsOn(testing.suites) -} diff --git a/dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt b/dokka-integration-tests/cli/src/cliIntegrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt similarity index 100% rename from dokka-integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt rename to dokka-integration-tests/cli/src/cliIntegrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt diff --git a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt index 14409663cc..0e532e1d49 100644 --- a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt +++ b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt @@ -5,11 +5,14 @@ package org.jetbrains.dokka.it.cli import org.jetbrains.dokka.it.AbstractIntegrationTest import org.jetbrains.dokka.it.systemProperty +import java.io.File public abstract class AbstractCliIntegrationTest : AbstractIntegrationTest() { /** The Dokka CLI JAR. */ protected val dokkaCliJarPath: String by systemProperty() /** Classpath required for running the Dokka CLI, delimited by `;`. */ - protected val dokkaPluginsClasspath: String by systemProperty() + protected val dokkaPluginsClasspath: String by systemProperty { + it.replace(File.pathSeparatorChar, ';') + } } diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index 896283c030..df4902b96b 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -4,17 +4,16 @@ @file:Suppress("UnstableApiUsage") import dokkabuild.tasks.GitCheckoutTask +import dokkabuild.utils.systemProperty +import org.gradle.api.tasks.PathSensitivity.NAME_ONLY import org.gradle.api.tasks.PathSensitivity.RELATIVE -import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL -import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED -import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled plugins { id("dokkabuild.kotlin-jvm") + id("dokkabuild.test-integration") id("dokkabuild.dev-maven-publish") - `jvm-test-suite` } dependencies { @@ -66,128 +65,76 @@ val aggregatingProject = gradle.includedBuild("dokka") val templateSettingsGradleKts = layout.projectDirectory.file("projects/template.settings.gradle.kts") val templateProjectsDir = layout.projectDirectory.dir("projects") -tasks.withType().configureEach { - setForkEvery(1) - maxHeapSize = "2G" - dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> - maxParallelForks = parallelism +testing.suites.withType().configureEach { + dependencies { + // test suites are independent by default (unlike the test source set), and must manually depend on the project + implementation(project()) } - - val useK2 = dokkaBuild.integrationTestUseK2.get() - - useJUnitPlatform { - if (useK2) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") - } - - systemProperty("org.jetbrains.dokka.experimental.tryK2", useK2) - // allow inspecting projects in temporary dirs after a test fails - systemProperty( - "junit.jupiter.tempdir.cleanup.mode.default", - dokkaBuild.isCI.map { isCi -> if (isCi) "ALWAYS" else "ON_SUCCESS" }.get(), - ) - - val dokkaVersion = provider { project.version.toString() } - inputs.property("dokkaVersion", dokkaVersion) - doFirst("set DOKKA_VERSION environment variable (workaround for https://github.com/gradle/gradle/issues/24267)") { - environment("DOKKA_VERSION", dokkaVersion.get()) - } - - // environment() isn't Provider API compatible yet https://github.com/gradle/gradle/issues/11534 - fun environmentProvider(name: String, provider: Provider) { - inputs.property(name, provider).optional(true) - provider.orNull?.let { environment(name, it) } - } - - environmentProvider("DOKKA_VERSION_OVERRIDE", dokkaBuild.integrationTestDokkaVersionOverride) - environmentProvider("isExhaustive", dokkaBuild.integrationTestExhaustive) - environmentProvider("ANDROID_HOME", dokkaBuild.androidSdkDir.map { it.invariantSeparatorsPath }) - - testLogging { - exceptionFormat = FULL - events(SKIPPED, FAILED) - showExceptions = true - showCauses = true - showStackTraces = true - } - - // The tests produce report data and generated Dokka output. - // Always cache them so Gradle can skip running integration tests if nothing has changed. - outputs.cacheIf("always cache") { true } } -testing { - suites { - withType().configureEach { - useJUnitJupiter() - - dependencies { - // test suites are independent by default (unlike the test source set), and must manually depend on the project - implementation(project()) - } - - targets.configureEach { - testTask.configure { - doFirst { - logger.info("running $path with javaLauncher:${javaLauncher.orNull?.metadata?.javaRuntimeVersion}") - } - } - } +// register a separate test suite for each 'template' project +registerTestProjectSuite( + "testTemplateProjectAndroid", + "it-android-0", + jvm = JavaLanguageVersion.of(17), // AGP requires JVM 17+ +) { + targets.configureEach { + testTask.configure { + // Just provide the Android SDK path as a system prop. + // Don't register as a Task input, because the path is different on everyone's machine, + // which means Gradle will never be able to cache the task. + jvmArgumentProviders.systemProperty( + "androidSdkDir", + dokkaBuild.androidSdkDir.map { it.invariantSeparatorsPath }, + ) } - - // register a separate test suite for each 'template' project - registerTestProjectSuite( - "testTemplateProjectAndroid", - "it-android-0", - jvm = JavaLanguageVersion.of(17), // AGP requires JVM 17+ - ) - registerTestProjectSuite("testTemplateProjectBasic", "it-basic") - registerTestProjectSuite("testTemplateProjectBasicGroovy", "it-basic-groovy") - registerTestProjectSuite("testTemplateProjectCollector", "it-collector-0") - registerTestProjectSuite("testTemplateProjectConfiguration", "it-configuration") - registerTestProjectSuite("testTemplateProjectJsIr", "it-js-ir-0") - registerTestProjectSuite("testTemplateProjectMultimodule0", "it-multimodule-0") - registerTestProjectSuite("testTemplateProjectMultimodule1", "it-multimodule-1") - registerTestProjectSuite("testTemplateProjectMultimoduleVersioning", "it-multimodule-versioning-0") - registerTestProjectSuite("testTemplateProjectMultimoduleInterModuleLinks", "it-multimodule-inter-module-links") - registerTestProjectSuite("testTemplateProjectMultiplatform", "it-multiplatform-0") - registerTestProjectSuite("testTemplateProjectTasksExecutionStress", "it-sequential-tasks-execution-stress") - registerTestProjectSuite("testTemplateProjectWasmBasic", "it-wasm-basic") - registerTestProjectSuite("testTemplateProjectWasmJsWasiBasic", "it-wasm-js-wasi-basic") - - registerTestProjectSuite( - "testExternalProjectKotlinxCoroutines", - "coroutines/kotlinx-coroutines", - jvm = JavaLanguageVersion.of(11) // kotlinx.coroutines requires JVM 11+ https://github.com/Kotlin/kotlinx.coroutines/issues/3665 - ) { - targets.configureEach { - testTask.configure { - dependsOn(checkoutKotlinxCoroutines) - // register the whole directory as an input because it contains the git diff - inputs - .dir(templateProjectsDir.file("coroutines")) - .withPropertyName("coroutinesProjectDir") - } - } + } +} +registerTestProjectSuite("testTemplateProjectBasic", "it-basic") +registerTestProjectSuite("testTemplateProjectBasicGroovy", "it-basic-groovy") +registerTestProjectSuite("testTemplateProjectCollector", "it-collector-0") +registerTestProjectSuite("testTemplateProjectConfiguration", "it-configuration") +registerTestProjectSuite("testTemplateProjectJsIr", "it-js-ir-0") +registerTestProjectSuite("testTemplateProjectMultimodule0", "it-multimodule-0") +registerTestProjectSuite("testTemplateProjectMultimodule1", "it-multimodule-1") +registerTestProjectSuite("testTemplateProjectMultimoduleVersioning", "it-multimodule-versioning-0") +registerTestProjectSuite("testTemplateProjectMultimoduleInterModuleLinks", "it-multimodule-inter-module-links") +registerTestProjectSuite("testTemplateProjectMultiplatform", "it-multiplatform-0") +registerTestProjectSuite("testTemplateProjectTasksExecutionStress", "it-sequential-tasks-execution-stress") +registerTestProjectSuite("testTemplateProjectWasmBasic", "it-wasm-basic") +registerTestProjectSuite("testTemplateProjectWasmJsWasiBasic", "it-wasm-js-wasi-basic") + +registerTestProjectSuite( + "testExternalProjectKotlinxCoroutines", + "coroutines/kotlinx-coroutines", + jvm = JavaLanguageVersion.of(11) // kotlinx.coroutines requires JVM 11+ https://github.com/Kotlin/kotlinx.coroutines/issues/3665 +) { + targets.configureEach { + testTask.configure { + dependsOn(checkoutKotlinxCoroutines) + // register the whole directory as an input because it contains the git diff + inputs + .dir(templateProjectsDir.file("coroutines")) + .withPropertyName("coroutinesProjectDir") } - registerTestProjectSuite( - "testExternalProjectKotlinxSerialization", - "serialization/kotlinx-serialization", - jvm = JavaLanguageVersion.of(11) // https://github.com/Kotlin/kotlinx.serialization/blob/1116f5f13a957feecda47d5e08b0aa335fc010fa/gradle/configure-source-sets.gradle#L9 - ) { - targets.configureEach { - testTask.configure { - dependsOn(checkoutKotlinxSerialization) - // register the whole directory as an input because it contains the git diff - inputs - .dir(templateProjectsDir.file("serialization")) - .withPropertyName("serializationProjectDir") - } - } + } +} +registerTestProjectSuite( + "testExternalProjectKotlinxSerialization", + "serialization/kotlinx-serialization", + jvm = JavaLanguageVersion.of(11) // https://github.com/Kotlin/kotlinx.serialization/blob/1116f5f13a957feecda47d5e08b0aa335fc010fa/gradle/configure-source-sets.gradle#L9 +) { + targets.configureEach { + testTask.configure { + dependsOn(checkoutKotlinxSerialization) + // register the whole directory as an input because it contains the git diff + inputs + .dir(templateProjectsDir.file("serialization")) + .withPropertyName("serializationProjectDir") } - registerTestProjectSuite("testUiShowcaseProject", "ui-showcase") } } - +registerTestProjectSuite("testUiShowcaseProject", "ui-showcase") /** * Create a new [JvmTestSuite] for a Gradle project. @@ -195,7 +142,7 @@ testing { * @param[projectPath] path to the Gradle project that will be tested by this suite, relative to [templateProjectsDir]. * The directory will be passed as a system property, `templateProjectDir`. */ -fun TestingExtension.registerTestProjectSuite( +fun registerTestProjectSuite( name: String, projectPath: String, jvm: JavaLanguageVersion? = null, @@ -203,38 +150,23 @@ fun TestingExtension.registerTestProjectSuite( ) { val templateProjectDir = templateProjectsDir.dir(projectPath) - suites.register(name) { + testing.suites.register(name) { targets.configureEach { testTask.configure { - // Register the project dir as a specific input, so changes in other projects don't affect the caching of this test - inputs.dir(templateProjectDir) - .withPropertyName("templateProjectDir") + // Pass the template dir in as a property, so it is accessible in tests. + systemProperty + .inputDirectory("templateProjectDir", templateProjectDir) .withPathSensitivity(RELATIVE) - // Pass the template dir in as a property, it is accessible in tests. - systemProperty("templateProjectDir", templateProjectDir.asFile.invariantSeparatorsPath) - - inputs.file(templateSettingsGradleKts) - .withPropertyName("templateSettingsGradleKts") - .withPathSensitivity(RELATIVE) - systemProperty( - "templateSettingsGradleKts", - templateSettingsGradleKts.asFile.invariantSeparatorsPath, - ) + systemProperty + .inputFile("templateSettingsGradleKts", templateSettingsGradleKts) + .withPathSensitivity(NAME_ONLY) devMavenPublish.configureTask(this) if (jvm != null) { javaLauncher = javaToolchains.launcherFor { languageVersion = jvm } } - - // For validation, on CI the generated output is uploaded, so the test must produce output in - // DOKKA_TEST_OUTPUT_PATH. For Gradle up-to-date checks the output dir must be specified. - val testOutputPath = System.getenv("DOKKA_TEST_OUTPUT_PATH") - inputs.property("testOutputPath", testOutputPath).optional(true) - if (testOutputPath != null) { - outputs.dir(testOutputPath).withPropertyName("testOutput") - } } } configure() @@ -271,12 +203,6 @@ val testAllExternalProjects by tasks.registering { doNotTrackState("lifecycle task, should always run") } -val integrationTest by tasks.registering { - description = "Lifecycle task for running integration tests" - // TODO KT-64200 - Refactor Maven and CLI integration tests to use Test Suites - // - Reimplement dokkabuild.test-integration.gradle.kts so that `integrationTest` is defined once there - dependsOn(tasks.withType()) // all tests in this project are integration tests -} //endregion val checkoutKotlinxCoroutines by tasks.registering(GitCheckoutTask::class) { diff --git a/dokka-integration-tests/gradle/projects/it-sequential-tasks-execution-stress/build.gradle.kts b/dokka-integration-tests/gradle/projects/it-sequential-tasks-execution-stress/build.gradle.kts index 9e686b6f7b..bec767f252 100644 --- a/dokka-integration-tests/gradle/projects/it-sequential-tasks-execution-stress/build.gradle.kts +++ b/dokka-integration-tests/gradle/projects/it-sequential-tasks-execution-stress/build.gradle.kts @@ -1,7 +1,6 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ - import org.jetbrains.dokka.gradle.DokkaTask import org.jetbrains.dokka.gradle.kotlinSourceSet import org.jetbrains.dokka.base.DokkaBase @@ -16,7 +15,8 @@ plugins { buildscript { dependencies { - classpath("org.jetbrains.dokka:dokka-base:${System.getenv("DOKKA_VERSION")}") + val dokka_it_dokka_version: String by project + classpath("org.jetbrains.dokka:dokka-base:$dokka_it_dokka_version") } } diff --git a/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt b/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt index 18539b2cf1..219a94e06f 100644 --- a/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt +++ b/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt @@ -9,6 +9,7 @@ import org.gradle.testkit.runner.GradleRunner import org.gradle.tooling.GradleConnectionException import org.gradle.util.GradleVersion import org.jetbrains.dokka.it.AbstractIntegrationTest +import org.jetbrains.dokka.it.optionalSystemProperty import org.jetbrains.dokka.it.systemProperty import java.io.File import java.net.URI @@ -39,7 +40,8 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() { fun createGradleRunner( buildVersions: BuildVersions, vararg arguments: String, - jvmArgs: List = listOf("-Xmx2G", "-XX:MaxMetaspaceSize=1G") + environmentVariables: Map = emptyMap(), + jvmArgs: List = listOf("-Xmx2G", "-XX:MaxMetaspaceSize=1G"), ): GradleRunner { // TODO quick hack to add `android { namespace }` on AGP 7+ (it's mandatory in 8+). @@ -65,6 +67,7 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() { .withJetBrainsCachedGradleVersion(buildVersions.gradleVersion) .withTestKitDir(File("build", "gradle-test-kit").absoluteFile) .withDebug(TestEnvironment.isEnabledDebug) + .withEnvironment(environmentVariables) .withArguments( listOfNotNull( "-Pdokka_it_dokka_version=${dokkaVersion}", @@ -103,8 +106,8 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() { } companion object { - private val dokkaVersionOverride: String? = System.getenv("DOKKA_VERSION_OVERRIDE") - private val dokkaVersion: String = dokkaVersionOverride ?: System.getenv("DOKKA_VERSION") + private val dokkaVersionOverride: String? by optionalSystemProperty() + private val dokkaVersion: String by systemProperty { dokkaVersionOverride ?: it } /** * Location of the template project that will be copied into [AbstractIntegrationTest.projectDir]. diff --git a/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/TestEnvironment.kt b/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/TestEnvironment.kt index b7e4c06f0f..09aba26f20 100644 --- a/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/TestEnvironment.kt +++ b/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/TestEnvironment.kt @@ -4,14 +4,13 @@ package org.jetbrains.dokka.it.gradle +import org.jetbrains.dokka.it.systemProperty import org.junit.jupiter.api.Tag object TestEnvironment { const val TRY_K2: String = "org.jetbrains.dokka.experimental.tryK2" - val isExhaustive: Boolean = checkNotNull(System.getenv("isExhaustive")) { - "Missing `isExhaustive` environment variable" - }.toBoolean() + val isExhaustive: Boolean by systemProperty(String::toBoolean) val isEnabledDebug: Boolean = System.getenv("ENABLE_DEBUG").toBoolean() diff --git a/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt b/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt index f3885d33d6..635e56efd0 100644 --- a/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt +++ b/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt @@ -5,10 +5,14 @@ package org.jetbrains.dokka.it.gradle import org.gradle.testkit.runner.TaskOutcome +import org.jetbrains.dokka.it.optionalSystemProperty +import org.junit.jupiter.api.Assumptions.abort +import org.junit.jupiter.api.Assumptions.assumeTrue import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource import java.io.File -import kotlin.test.BeforeTest +import kotlin.io.path.Path +import kotlin.io.path.isDirectory import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -18,31 +22,26 @@ internal class AndroidTestedVersionsArgumentsProvider : TestedVersionsArgumentsP class Android0GradleIntegrationTest : AbstractGradleIntegrationTest() { companion object { - /** - * Indicating whether or not the current machine executing the test is a CI - */ - private val isCI: Boolean get() = System.getenv("CI") == "true" - - private val isAndroidSdkInstalled: Boolean = System.getenv("ANDROID_SDK_ROOT") != null || - System.getenv("ANDROID_HOME") != null - - fun assumeAndroidSdkInstalled() { - if (isCI) return - if (!isAndroidSdkInstalled) { - throw IllegalStateException("Expected Android SDK is installed") - } - } + private val androidSdkDir: String? by optionalSystemProperty() } - @BeforeTest - fun prepareAndroidProjectFiles() { - assumeAndroidSdkInstalled() + private fun getAndroidSdkDir(): String { + // skip the test if androidSdkDir is not present + val androidSdkDir = androidSdkDir ?: abort("test requires androidSdkDir is present, but was null") + assumeTrue(Path(androidSdkDir).isDirectory(), "androidSdkDir must be a directory") + return androidSdkDir } @ParameterizedTest(name = "{0}") @ArgumentsSource(AndroidTestedVersionsArgumentsProvider::class) fun execute(buildVersions: BuildVersions) { - val result = createGradleRunner(buildVersions, "dokkaHtml", "-i", "-s").buildRelaxed() + val result = createGradleRunner( + buildVersions, + "dokkaHtml", "-i", "-s", + environmentVariables = mapOf( + "ANDROID_HOME" to getAndroidSdkDir() + ), + ).buildRelaxed() assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome) val htmlOutputDir = File(projectDir, "build/dokka/html") diff --git a/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt b/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt index 88ae4351ae..fb9e134eda 100644 --- a/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt +++ b/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt @@ -28,7 +28,7 @@ class SequentialTasksExecutionStressTest : AbstractGradleIntegrationTest() { "runTasks", "--info", "--stacktrace", - "-Ptask_number=100", + "-Ptask_number=50", jvmArgs = listOf("-Xmx1G", "-XX:MaxMetaspaceSize=500m") ).buildRelaxed() diff --git a/dokka-integration-tests/maven/build.gradle.kts b/dokka-integration-tests/maven/build.gradle.kts index f92ced62df..6e8044594e 100644 --- a/dokka-integration-tests/maven/build.gradle.kts +++ b/dokka-integration-tests/maven/build.gradle.kts @@ -4,23 +4,23 @@ @file:Suppress("UnstableApiUsage") import dokkabuild.tasks.GitCheckoutTask +import dokkabuild.utils.systemProperty +import org.gradle.api.tasks.PathSensitivity.NAME_ONLY import org.gradle.api.tasks.PathSensitivity.RELATIVE -import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL -import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED -import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP plugins { id("dokkabuild.kotlin-jvm") id("dokkabuild.dev-maven-publish") id("dokkabuild.setup-maven-cli") + id("dokkabuild.test-integration") `jvm-test-suite` } dependencies { api(projects.utilities) - api(kotlin("test-junit5")) + api(libs.kotlin.test) api(libs.junit.jupiterApi) val dokkaVersion = project.version.toString() @@ -51,106 +51,31 @@ dependencies { val templateProjectsDir = layout.projectDirectory.dir("projects") -/** - * Provide files required for running Dokka CLI in a build cache friendly way. - */ -abstract class MvnBinaryPathArgProvider : CommandLineArgumentProvider { - @get:Classpath - abstract val maven: RegularFileProperty - - override fun asArguments(): Iterable = buildList { - add("-D" + "mavenBinaryFile=" + maven.asFile.get().absolutePath) - } -} - - tasks.withType().configureEach { - setForkEvery(1) - maxHeapSize = "2G" - dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> - maxParallelForks = parallelism - } - - val useK2 = dokkaBuild.integrationTestUseK2.get() - - useJUnitPlatform { - if (useK2) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") - } - - systemProperty("org.jetbrains.dokka.experimental.tryK2", useK2) - // allow inspecting projects in temporary dirs after a test fails - systemProperty( - "junit.jupiter.tempdir.cleanup.mode.default", - if (dokkaBuild.isCI.get()) "ALWAYS" else "ON_SUCCESS", - ) - - val dokkaVersion = provider { project.version.toString() } - inputs.property("dokkaVersion", dokkaVersion) - doFirst("set DOKKA_VERSION environment variable (workaround for https://github.com/gradle/gradle/issues/24267)") { - environment("DOKKA_VERSION", dokkaVersion.get()) - } - - // environment() isn't Provider API compatible yet https://github.com/gradle/gradle/issues/11534 - dokkaBuild.integrationTestExhaustive.orNull?.let { exhaustive -> - environment("isExhaustive", exhaustive) - } - dependsOn(tasks.installMavenBinary) - jvmArgumentProviders.add( - objects.newInstance().apply { - maven = mavenCliSetup.mvn - } - ) - - testLogging { - exceptionFormat = FULL - events(SKIPPED, FAILED) - showExceptions = true - showCauses = true - showStackTraces = true - } - - // The tests produce report data and generated Dokka output. - // Always cache them so Gradle can skip running integration tests if nothing has changed. - outputs.cacheIf("always cache") { true } + systemProperty.inputFile("mavenBinaryFile", mavenCliSetup.mvn) + .withPathSensitivity(NAME_ONLY) } -testing { - suites { - withType().configureEach { - useJUnitJupiter() - - dependencies { - // test suites are independent by default (unlike the test source set), and must manually depend on the project - implementation(project()) - } - - targets.configureEach { - testTask.configure { - doFirst { - logger.info("running $path with javaLauncher:${javaLauncher.orNull?.metadata?.javaRuntimeVersion}") - } - } - } - } - - registerTestProjectSuite("testTemplateProjectMaven", "it-maven") - registerTestProjectSuite("testExternalProjectBioJava", "biojava/biojava") { - targets.configureEach { - testTask.configure { - dependsOn(checkoutBioJava) - // register the whole directory as an input because it contains the git diff - inputs - .dir(templateProjectsDir.file("biojava")) - .withPropertyName("biojavaProjectDir") - } - } - } +testing.suites.withType().configureEach { + dependencies { + // test suites are independent by default (unlike the test source set), and must manually depend on the project + implementation(project()) } } -tasks.check { - dependsOn(testing.suites) +registerTestProjectSuite("testTemplateProjectMaven", "it-maven") +registerTestProjectSuite("testExternalProjectBioJava", "biojava/biojava") { + targets.configureEach { + testTask.configure { + dependsOn(checkoutBioJava) + // register the whole directory as an input because it contains the git diff + inputs + .dir(templateProjectsDir.file("biojava")) + .withPropertyName("biojavaProjectDir") + .withPathSensitivity(RELATIVE) + } + } } /** @@ -159,7 +84,7 @@ tasks.check { * @param[projectPath] path to the Gradle project that will be tested by this suite, relative to [templateProjectsDir]. * The directory will be passed as a system property, `templateProjectDir`. */ -fun TestingExtension.registerTestProjectSuite( +fun registerTestProjectSuite( name: String, projectPath: String, jvm: JavaLanguageVersion? = null, @@ -167,30 +92,19 @@ fun TestingExtension.registerTestProjectSuite( ) { val templateProjectDir = templateProjectsDir.dir(projectPath) - suites.register(name) { + testing.suites.register(name) { targets.configureEach { testTask.configure { - // Register the project dir as a specific input, so changes in other projects don't affect the caching of this test - inputs.dir(templateProjectDir) - .withPropertyName("templateProjectDir") - .withPathSensitivity(RELATIVE) - // Pass the template dir in as a property, it is accessible in tests. - systemProperty("templateProjectDir", templateProjectDir.asFile.invariantSeparatorsPath) + systemProperty + .inputDirectory("templateProjectDir", templateProjectDir) + .withPathSensitivity(RELATIVE) devMavenPublish.configureTask(this) if (jvm != null) { javaLauncher = javaToolchains.launcherFor { languageVersion = jvm } } - - // For validation, on CI the generated output is uploaded, so the test must produce output in - // DOKKA_TEST_OUTPUT_PATH. For Gradle up-to-date checks the output dir must be specified. - val testOutputPath = System.getenv("DOKKA_TEST_OUTPUT_PATH") - inputs.property("testOutputPath", testOutputPath).optional(true) - if (testOutputPath != null) { - outputs.dir(testOutputPath).withPropertyName("testOutput") - } } } configure() @@ -227,10 +141,7 @@ val testAllExternalProjects by tasks.registering { doNotTrackState("lifecycle task, should always run") } -val integrationTest by tasks.registering { - description = "Lifecycle task for running integration tests" - // TODO - Refactor Maven and CLI integration tests to use Test Suites - // - Reimplement dokkabuild.test-integration.gradle.kts so that `integrationTest` is defined once there +tasks.integrationTest { dependsOn(tasks.withType()) // all tests in this project are integration tests } //endregion diff --git a/dokka-integration-tests/maven/src/testExternalProjectBioJava/kotlin/BiojavaIntegrationTest.kt b/dokka-integration-tests/maven/src/testExternalProjectBioJava/kotlin/BiojavaIntegrationTest.kt index 48d3d90b87..749ad98efb 100644 --- a/dokka-integration-tests/maven/src/testExternalProjectBioJava/kotlin/BiojavaIntegrationTest.kt +++ b/dokka-integration-tests/maven/src/testExternalProjectBioJava/kotlin/BiojavaIntegrationTest.kt @@ -13,7 +13,7 @@ import kotlin.test.assertTrue class BiojavaIntegrationTest : AbstractIntegrationTest(), TestOutputCopier { - private val currentDokkaVersion: String = checkNotNull(System.getenv("DOKKA_VERSION")) + private val dokkaVersion: String by systemProperty() private val mavenBinaryFile: File by systemProperty { File(it) } override val projectOutputLocation: File by lazy { File(projectDir, "biojava-core/target/dokkaJavadoc") } @@ -45,7 +45,7 @@ class BiojavaIntegrationTest : AbstractIntegrationTest(), TestOutputCopier { "--settings", localSettingsXml.invariantSeparatorsPath, "-pl", "biojava-core", - "\"-Ddokka_version=$currentDokkaVersion\"", + "\"-Ddokka_version=$dokkaVersion\"", "-U", "-e" ).start().awaitProcessResult() diff --git a/dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt b/dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt index 8795153e1b..d4f52be203 100644 --- a/dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt +++ b/dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt @@ -13,7 +13,7 @@ import kotlin.test.* class MavenIntegrationTest : AbstractIntegrationTest() { - private val currentDokkaVersion: String = checkNotNull(System.getenv("DOKKA_VERSION")) + private val dokkaVersion: String by systemProperty() private val mavenBinaryFile: File by systemProperty { File(it) } @@ -30,7 +30,7 @@ class MavenIntegrationTest : AbstractIntegrationTest() { val pomXml = File(projectDir, "pom.xml") assertTrue(pomXml.isFile) pomXml.apply { - writeText(readText().replace("\$dokka_version", currentDokkaVersion)) + writeText(readText().replace("\$dokka_version", dokkaVersion)) } val customResourcesDir = File(templateProjectDir, "customResources") if (customResourcesDir.exists() && customResourcesDir.isDirectory) { diff --git a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt index 74caf38078..afb7a41d2f 100644 --- a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt +++ b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt @@ -28,3 +28,24 @@ fun systemProperty( } convert(value) } + +/** + * Delegated accessor for a system property. + * + * @see System.getProperty + */ +fun optionalSystemProperty(): ReadOnlyProperty = + optionalSystemProperty { it } + +/** + * Delegated accessor for a system property. + * + * @see System.getProperty + */ +fun optionalSystemProperty( + convert: (value: String?) -> T +): ReadOnlyProperty = + ReadOnlyProperty { _, property -> + val value: String? = System.getProperty(property.name) + convert(value) + } diff --git a/settings.gradle.kts b/settings.gradle.kts index 3aaa0b5029..68fe3f9810 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -120,4 +120,4 @@ dependencyResolutionManagement { } } } -} \ No newline at end of file +} From b91268f74c79066ca37fbd7f94dc5dab8b3fbd51 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 4 Jun 2024 11:55:04 +0200 Subject: [PATCH 14/31] fail test if Android SDK missing --- dokka-integration-tests/gradle/build.gradle.kts | 2 +- .../kotlin/Android0GradleIntegrationTest.kt | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index df4902b96b..88380463a0 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -81,7 +81,7 @@ registerTestProjectSuite( targets.configureEach { testTask.configure { // Just provide the Android SDK path as a system prop. - // Don't register as a Task input, because the path is different on everyone's machine, + // Don't register it as a Task input, because the path is different on everyone's machine, // which means Gradle will never be able to cache the task. jvmArgumentProviders.systemProperty( "androidSdkDir", diff --git a/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt b/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt index 635e56efd0..ce4a250ada 100644 --- a/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt +++ b/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt @@ -6,8 +6,6 @@ package org.jetbrains.dokka.it.gradle import org.gradle.testkit.runner.TaskOutcome import org.jetbrains.dokka.it.optionalSystemProperty -import org.junit.jupiter.api.Assumptions.abort -import org.junit.jupiter.api.Assumptions.assumeTrue import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource import java.io.File @@ -26,9 +24,9 @@ class Android0GradleIntegrationTest : AbstractGradleIntegrationTest() { } private fun getAndroidSdkDir(): String { - // skip the test if androidSdkDir is not present - val androidSdkDir = androidSdkDir ?: abort("test requires androidSdkDir is present, but was null") - assumeTrue(Path(androidSdkDir).isDirectory(), "androidSdkDir must be a directory") + val androidSdkDir = androidSdkDir + assertNotNull(androidSdkDir, "androidSdkDir missing") + assertTrue(Path(androidSdkDir).isDirectory(), "androidSdkDir is not a directory") return androidSdkDir } From 5f094e74bb33ba2f2f3c82c43d3b197f45ba6007 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:09:10 +0200 Subject: [PATCH 15/31] bump jgit --- .../gradle/projects/coroutines/kotlinx-coroutines | 1 - gradle/libs.versions.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 160000 dokka-integration-tests/gradle/projects/coroutines/kotlinx-coroutines diff --git a/dokka-integration-tests/gradle/projects/coroutines/kotlinx-coroutines b/dokka-integration-tests/gradle/projects/coroutines/kotlinx-coroutines deleted file mode 160000 index 1a0287ca3f..0000000000 --- a/dokka-integration-tests/gradle/projects/coroutines/kotlinx-coroutines +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1a0287ca3fb5d6c59594d62131e878da4929c5f8 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9d0dfd7665..acf0d138a8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,7 +62,7 @@ gradlePlugin-gradle-enterprise = "3.16.2" ## Test junit = "5.9.3" kotest = "5.6.2" -eclipse-jgit = "5.13.2.202306221912-r" # jgit 6.X requires Java 11 to run +eclipse-jgit = "5.13.3.202401111512-r" # jgit 6.X requires Java 11 to run [libraries] From 4838b8910699d0881a082c99161acea03d1e8d95 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:15:32 +0200 Subject: [PATCH 16/31] fix devMavenRepositories caching --- .../dokkabuild/DevMavenPublishExtension.kt | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt index 8076318c53..a86724ca88 100644 --- a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt +++ b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt @@ -6,10 +6,10 @@ package dokkabuild import dokkabuild.utils.systemProperty import org.gradle.api.Task import org.gradle.api.file.FileCollection -import org.gradle.api.file.FileSystemLocation import org.gradle.api.provider.Provider import org.gradle.api.tasks.PathSensitivity.RELATIVE import org.gradle.process.JavaForkOptions +import java.io.File abstract class DevMavenPublishExtension( /** @@ -23,21 +23,19 @@ abstract class DevMavenPublishExtension( /** * Files suitable for registering as a task input (as in, the files are reproducible-build compatible). */ - private val devMavenRepositoriesInputFiles: Provider> = + private val devMavenRepositoriesInputFiles: Provider> = devMavenRepositories + // Convert to a FileTree, so we can filter out files. + .asFileTree + // Exclude Maven Metadata files because they contain timestamps, meaning tasks that use + // devMavenRepositories as an input will never be up-to-date. + // The Gradle Module Metadata contains the same information (and more), + // so the Maven metadata is redundant. + .matching { exclude("**/maven-metadata*.xml") } + // FileTrees have an unstable order (even on the same machine), which means Gradle up-to-date checks fail. + // So, manually sort the files so that Gradle can cache the task. .elements - .map { files -> - files - .filterNot { - // Exclude Maven Metadata files because they contain timestamps, meaning tasks that use - // devMavenRepositories as an input will never be up-to-date. - // The Gradle Module Metadata contains the same information (and more), - // so the Maven metadata is redundant. - it.asFile.name.run { startsWith("maven-metadata") && endsWith(".xml") } - } - // sort the elements to ensure that the contents are reproducible - .toSortedSet { a, b -> a.asFile.compareTo(b.asFile) } - } + .map { files -> files.map { it.asFile }.sorted() } /** * Configures [task] to register [devMavenRepositories] as a task input, @@ -47,7 +45,6 @@ abstract class DevMavenPublishExtension( task.inputs.files(devMavenRepositoriesInputFiles) .withPropertyName("devMavenPublish.devMavenRepositoriesInputFiles") .withPathSensitivity(RELATIVE) - .ignoreEmptyDirectories() task.dependsOn(devMavenRepositories) @@ -55,7 +52,7 @@ abstract class DevMavenPublishExtension( task.jvmArgumentProviders.systemProperty( "devMavenRepositories", devMavenRepositoriesInputFiles.map { paths -> - paths.joinToString(",") { it.asFile.invariantSeparatorsPath } + paths.joinToString(",") { it.absoluteFile.invariantSeparatorsPath } }, ) } From 839c7b8ec72388be55243ad9e5c732002152a553 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:40:30 +0200 Subject: [PATCH 17/31] fix devMavenRepositories caching --- .../main/kotlin/dokkabuild/DevMavenPublishExtension.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt index a86724ca88..1728117e5d 100644 --- a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt +++ b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt @@ -25,7 +25,7 @@ abstract class DevMavenPublishExtension( */ private val devMavenRepositoriesInputFiles: Provider> = devMavenRepositories - // Convert to a FileTree, so we can filter out files. + // Convert to a FileTree, which converts directories to all files, so we can filter on specific files. .asFileTree // Exclude Maven Metadata files because they contain timestamps, meaning tasks that use // devMavenRepositories as an input will never be up-to-date. @@ -51,9 +51,9 @@ abstract class DevMavenPublishExtension( if (task is JavaForkOptions) { task.jvmArgumentProviders.systemProperty( "devMavenRepositories", - devMavenRepositoriesInputFiles.map { paths -> - paths.joinToString(",") { it.absoluteFile.invariantSeparatorsPath } - }, + devMavenRepositories.elements.map { paths -> + paths.joinToString(",") { it.asFile.canonicalFile.invariantSeparatorsPath } + } ) } } From 2332805258bf63d2c28cdf8450e6ad2402ac8c8d Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:02:53 +0200 Subject: [PATCH 18/31] fix ANDROID_HOME in AndroidIT --- .../gradle/build.gradle.kts | 8 ++--- .../gradle/AbstractGradleIntegrationTest.kt | 2 -- .../kotlin/Android0GradleIntegrationTest.kt | 35 ++++++++++--------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index 88380463a0..c16976eabc 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -80,13 +80,9 @@ registerTestProjectSuite( ) { targets.configureEach { testTask.configure { - // Just provide the Android SDK path as a system prop. - // Don't register it as a Task input, because the path is different on everyone's machine, + // Don't register ANDROID_HOME as a Task input, because the path is different on everyone's machine, // which means Gradle will never be able to cache the task. - jvmArgumentProviders.systemProperty( - "androidSdkDir", - dokkaBuild.androidSdkDir.map { it.invariantSeparatorsPath }, - ) + environment("ANDROID_HOME", dokkaBuild.androidSdkDir.get().invariantSeparatorsPath) } } } diff --git a/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt b/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt index 219a94e06f..b0e4db7f21 100644 --- a/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt +++ b/dokka-integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt @@ -40,7 +40,6 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() { fun createGradleRunner( buildVersions: BuildVersions, vararg arguments: String, - environmentVariables: Map = emptyMap(), jvmArgs: List = listOf("-Xmx2G", "-XX:MaxMetaspaceSize=1G"), ): GradleRunner { @@ -67,7 +66,6 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() { .withJetBrainsCachedGradleVersion(buildVersions.gradleVersion) .withTestKitDir(File("build", "gradle-test-kit").absoluteFile) .withDebug(TestEnvironment.isEnabledDebug) - .withEnvironment(environmentVariables) .withArguments( listOfNotNull( "-Pdokka_it_dokka_version=${dokkaVersion}", diff --git a/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt b/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt index ce4a250ada..f3885d33d6 100644 --- a/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt +++ b/dokka-integration-tests/gradle/src/testTemplateProjectAndroid/kotlin/Android0GradleIntegrationTest.kt @@ -5,12 +5,10 @@ package org.jetbrains.dokka.it.gradle import org.gradle.testkit.runner.TaskOutcome -import org.jetbrains.dokka.it.optionalSystemProperty import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ArgumentsSource import java.io.File -import kotlin.io.path.Path -import kotlin.io.path.isDirectory +import kotlin.test.BeforeTest import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertTrue @@ -20,26 +18,31 @@ internal class AndroidTestedVersionsArgumentsProvider : TestedVersionsArgumentsP class Android0GradleIntegrationTest : AbstractGradleIntegrationTest() { companion object { - private val androidSdkDir: String? by optionalSystemProperty() + /** + * Indicating whether or not the current machine executing the test is a CI + */ + private val isCI: Boolean get() = System.getenv("CI") == "true" + + private val isAndroidSdkInstalled: Boolean = System.getenv("ANDROID_SDK_ROOT") != null || + System.getenv("ANDROID_HOME") != null + + fun assumeAndroidSdkInstalled() { + if (isCI) return + if (!isAndroidSdkInstalled) { + throw IllegalStateException("Expected Android SDK is installed") + } + } } - private fun getAndroidSdkDir(): String { - val androidSdkDir = androidSdkDir - assertNotNull(androidSdkDir, "androidSdkDir missing") - assertTrue(Path(androidSdkDir).isDirectory(), "androidSdkDir is not a directory") - return androidSdkDir + @BeforeTest + fun prepareAndroidProjectFiles() { + assumeAndroidSdkInstalled() } @ParameterizedTest(name = "{0}") @ArgumentsSource(AndroidTestedVersionsArgumentsProvider::class) fun execute(buildVersions: BuildVersions) { - val result = createGradleRunner( - buildVersions, - "dokkaHtml", "-i", "-s", - environmentVariables = mapOf( - "ANDROID_HOME" to getAndroidSdkDir() - ), - ).buildRelaxed() + val result = createGradleRunner(buildVersions, "dokkaHtml", "-i", "-s").buildRelaxed() assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome) val htmlOutputDir = File(projectDir, "build/dokka/html") From eaaf9479da7cab9aa7c3ff506dbf2cca49a21854 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 5 Jun 2024 11:54:46 +0200 Subject: [PATCH 19/31] move Dokka CLI JAR check into test (simplifies the Gradle config) --- dokka-integration-tests/cli/build.gradle.kts | 10 +--------- .../dokka/it/cli/AbstractCliIntegrationTest.kt | 6 +++++- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 64a5c942d5..7b871d663d 100644 --- a/dokka-integration-tests/cli/build.gradle.kts +++ b/dokka-integration-tests/cli/build.gradle.kts @@ -52,16 +52,8 @@ testing { targets.configureEach { testTask.configure { - - val dokkaCliJar = configurations.dokkaCliResolver - .map { files -> - requireNotNull(files.singleOrNull()) { - "Expected a single Dokka CLI JAR, but got ${files.count()}" - } - } - systemProperty - .inputFile("dokkaCliJarPath", dokkaCliJar) + .inputFiles("dokkaCliJarPath", configurations.dokkaCliResolver) .withNormalizer(ClasspathNormalizer::class) systemProperty diff --git a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt index 0e532e1d49..fa284bc2cd 100644 --- a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt +++ b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt @@ -9,7 +9,11 @@ import java.io.File public abstract class AbstractCliIntegrationTest : AbstractIntegrationTest() { /** The Dokka CLI JAR. */ - protected val dokkaCliJarPath: String by systemProperty() + protected val dokkaCliJarPath: String by systemProperty { + it.split(File.pathSeparatorChar) + .singleOrNull() + ?: error("Expected a single Dokka CLI JAR, but got $it") + } /** Classpath required for running the Dokka CLI, delimited by `;`. */ protected val dokkaPluginsClasspath: String by systemProperty { From 7d1efb55145871112cc655b686121a33e825e4c7 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:38:26 +0200 Subject: [PATCH 20/31] revert task_number --- dokka-integration-tests/cli/build.gradle.kts | 1 - .../kotlin/SequentialTasksExecutionStressTest.kt | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 7b871d663d..5fdf4b3e9d 100644 --- a/dokka-integration-tests/cli/build.gradle.kts +++ b/dokka-integration-tests/cli/build.gradle.kts @@ -49,7 +49,6 @@ testing { } register("cliIntegrationTest") { - targets.configureEach { testTask.configure { systemProperty diff --git a/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt b/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt index fb9e134eda..88ae4351ae 100644 --- a/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt +++ b/dokka-integration-tests/gradle/src/testTemplateProjectTasksExecutionStress/kotlin/SequentialTasksExecutionStressTest.kt @@ -28,7 +28,7 @@ class SequentialTasksExecutionStressTest : AbstractGradleIntegrationTest() { "runTasks", "--info", "--stacktrace", - "-Ptask_number=50", + "-Ptask_number=100", jvmArgs = listOf("-Xmx1G", "-XX:MaxMetaspaceSize=500m") ).buildRelaxed() From 58e0cd0945c6437aa2c4462d2aacee46ba6b436d Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:48:51 +0200 Subject: [PATCH 21/31] update systemProperty/optionalSystemProperty kdoc --- .../kotlin/org/jetbrains/dokka/it/systemProperties.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt index afb7a41d2f..63f1df54cd 100644 --- a/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt +++ b/dokka-integration-tests/utilities/src/main/kotlin/org/jetbrains/dokka/it/systemProperties.kt @@ -7,7 +7,7 @@ package org.jetbrains.dokka.it import kotlin.properties.ReadOnlyProperty /** - * Delegated accessor for a system property. + * Delegated accessor for a system property with the same name as the property. * * @see System.getProperty */ @@ -15,7 +15,7 @@ fun systemProperty(): ReadOnlyProperty = systemProperty { it } /** - * Delegated accessor for a system property. + * Delegated accessor for a system property with the same name as the property. * * @see System.getProperty */ @@ -30,7 +30,7 @@ fun systemProperty( } /** - * Delegated accessor for a system property. + * Delegated accessor for an optional system property with the same name as the property. * * @see System.getProperty */ @@ -38,7 +38,7 @@ fun optionalSystemProperty(): ReadOnlyProperty = optionalSystemProperty { it } /** - * Delegated accessor for a system property. + * Delegated accessor for an optional system property with the same name as the property. * * @see System.getProperty */ From 43509f12bef7b5a3805d293e72068d5ee89fada5 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 5 Jun 2024 12:54:56 +0200 Subject: [PATCH 22/31] tidy SystemPropertyAdder --- .../dokkabuild/utils/SystemPropertyAdder.kt | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt index 2399d25f46..484a0af95f 100644 --- a/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt +++ b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt @@ -63,23 +63,17 @@ abstract class SystemPropertyAdder @Inject internal constructor( fun inputFile( key: String, - file: Provider, + file: Provider, ): TaskInputFilePropertyBuilder { task.jvmArgumentProviders.add( SystemPropertyArgumentProvider(key, file) { - it.orNull?.invariantSeparatorsPath + it.orNull?.asFile?.invariantSeparatorsPath } ) return task.inputs.file(file) .withPropertyName("SystemProperty input file $key") } - @JvmName("inputRegularFile") - fun inputFile( - key: String, - file: Provider, - ): TaskInputFilePropertyBuilder = inputFile(key, file.map { it.asFile }) - fun inputFiles( key: String, files: Provider, @@ -120,6 +114,16 @@ private class SystemPropertyArgumentProvider( } } +/** + * Add a System Property (in the format `-D$key=$value`). + * + * [value] will _not_ be registered as a Gradle [org.gradle.api.Task] input. + * (Which might be beneficial in cases where the property is optional, or not reproducible, + * as such a property might disrupt task caching.) + * + * If you want to register the property as a Task input, use the + * [Test.systemProperty][dokkabuild.utils.systemProperty] above instead. + */ fun MutableList.systemProperty( key: String, value: Provider, From 706acab9629929a7241be91e4079d18415bc3a83 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:04:06 +0200 Subject: [PATCH 23/31] remove check workarounds --- build.gradle.kts | 5 ----- dokka-integration-tests/build.gradle.kts | 6 ------ 2 files changed, 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c38244f3d7..2a6ac68dc2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -82,8 +82,3 @@ fun includedBuildTasks(taskName: String, filter: (IncludedBuild) -> Boolean = { .filter { it.name != "build-logic" } .filter(filter) .mapNotNull { it.task(":$taskName") } - - -tasks.check { - gradle.includedBuilds.forEach { dependsOn(it.task(":check")) } -} diff --git a/dokka-integration-tests/build.gradle.kts b/dokka-integration-tests/build.gradle.kts index cde8fa82bc..1c0e57d69b 100644 --- a/dokka-integration-tests/build.gradle.kts +++ b/dokka-integration-tests/build.gradle.kts @@ -32,9 +32,3 @@ fun subprojectTasks(taskName: String): List = subprojects .filter { it.getTasksByName(taskName, false).isNotEmpty() } .map { ":${it.name}:$taskName" } - -tasks.check { - subprojects.forEach { - it.tasks.findByName("check")?.let(Task::dependsOn) - } -} From 6f69b83d60bf9412c934ae4e2eabb504b1acd1a6 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:15:58 +0200 Subject: [PATCH 24/31] final bits of tidying and deduplicating integration test config --- .../dokkabuild.test-integration.gradle.kts | 38 ++++++++++++++++-- .../gradle/build.gradle.kts | 39 +++---------------- .../maven/build.gradle.kts | 36 ----------------- 3 files changed, 40 insertions(+), 73 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts index 7ce21aca0a..31a47e5654 100644 --- a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts @@ -7,6 +7,7 @@ import dokkabuild.utils.systemProperty import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED +import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP plugins { id("dokkabuild.base") @@ -32,10 +33,10 @@ tasks.withType().configureEach { val useK2 = dokkaBuild.integrationTestUseK2 systemProperty.inputProperty("org.jetbrains.dokka.experimental.tryK2", useK2) .optional(true) - useJUnitPlatform { if (useK2.get()) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") } + systemProperty.inputProperty("isExhaustive", dokkaBuild.integrationTestExhaustive) // allow inspecting projects in temporary dirs after a test fails @@ -44,9 +45,6 @@ tasks.withType().configureEach { dokkaBuild.isCI.map { if (it) "ALWAYS" else "ON_SUCCESS" } ) - inputs.property("ENABLE_DEBUG", providers.systemProperty("ENABLE_DEBUG")) - .optional(true) - testLogging { exceptionFormat = FULL events(SKIPPED, FAILED) @@ -91,3 +89,35 @@ integrationTest.configure { tasks.check { dependsOn(integrationTest) } + +//region project tests management + +// set up task ordering - template projects (which are generally faster) should be tested before external projects +val jvmTestTask = tasks.withType().matching { it.name == "test" } +val testTemplateProjectsTasks = tasks.withType().matching { it.name.startsWith("testTemplateProject") } +val testExternalProjectsTasks = tasks.withType().matching { it.name.startsWith("testExternalProject") } + +testTemplateProjectsTasks.configureEach { + shouldRunAfter(jvmTestTask) +} +testExternalProjectsTasks.configureEach { + shouldRunAfter(jvmTestTask) + shouldRunAfter(testTemplateProjectsTasks) +} + +// define lifecycle tasks for project tests +val testAllTemplateProjects by tasks.registering { + description = "Lifecycle task for running all template-project tests" + group = VERIFICATION_GROUP + dependsOn(testTemplateProjectsTasks) + doNotTrackState("lifecycle task, should always run") +} + +val testAllExternalProjects by tasks.registering { + description = "Lifecycle task for running all external-project tests" + group = VERIFICATION_GROUP + shouldRunAfter(testAllTemplateProjects) + dependsOn(testExternalProjectsTasks) + doNotTrackState("lifecycle task, should always run") +} +//endregion diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index c16976eabc..7b16972fd5 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -61,7 +61,12 @@ kotlin { } } -val aggregatingProject = gradle.includedBuild("dokka") +tasks.withType().configureEach { + val enableDebug = providers.environmentVariable("ENABLE_DEBUG") + inputs.property("enableDebug", enableDebug).optional(true) + environment("ENABLE_DEBUG", enableDebug.get()) +} + val templateSettingsGradleKts = layout.projectDirectory.file("projects/template.settings.gradle.kts") val templateProjectsDir = layout.projectDirectory.dir("projects") @@ -169,38 +174,6 @@ fun registerTestProjectSuite( } } -//region project tests management - -// set up task ordering - template projects (which are generally faster) should be tested before external projects -val testTemplateProjectsTasks = tasks.withType().matching { it.name.startsWith("testTemplateProject") } -val testExternalProjectsTasks = tasks.withType().matching { it.name.startsWith("testExternalProject") } - -testTemplateProjectsTasks.configureEach { - shouldRunAfter(tasks.test) -} -testExternalProjectsTasks.configureEach { - shouldRunAfter(tasks.test) - shouldRunAfter(testTemplateProjectsTasks) -} - -// define lifecycle tasks for project tests -val testAllTemplateProjects by tasks.registering { - description = "Lifecycle task for running all template-project tests" - group = VERIFICATION_GROUP - dependsOn(testTemplateProjectsTasks) - doNotTrackState("lifecycle task, should always run") -} - -val testAllExternalProjects by tasks.registering { - description = "Lifecycle task for running all external-project tests" - group = VERIFICATION_GROUP - shouldRunAfter(testAllTemplateProjects) - dependsOn(testExternalProjectsTasks) - doNotTrackState("lifecycle task, should always run") -} - -//endregion - val checkoutKotlinxCoroutines by tasks.registering(GitCheckoutTask::class) { uri = "https://github.com/Kotlin/kotlinx.coroutines.git" commitId = "b78bbf518bd8e90e9ed2133ebdacc36441210cd6" diff --git a/dokka-integration-tests/maven/build.gradle.kts b/dokka-integration-tests/maven/build.gradle.kts index 6e8044594e..bb38950913 100644 --- a/dokka-integration-tests/maven/build.gradle.kts +++ b/dokka-integration-tests/maven/build.gradle.kts @@ -50,7 +50,6 @@ dependencies { val templateProjectsDir = layout.projectDirectory.dir("projects") - tasks.withType().configureEach { dependsOn(tasks.installMavenBinary) systemProperty.inputFile("mavenBinaryFile", mavenCliSetup.mvn) @@ -111,41 +110,6 @@ fun registerTestProjectSuite( } } -//region project tests management - -// set up task ordering - template projects (which are generally faster) should be tested before external projects -val testTemplateProjectsTasks = tasks.withType().matching { it.name.startsWith("testTemplateProject") } -val testExternalProjectsTasks = tasks.withType().matching { it.name.startsWith("testExternalProject") } - -testTemplateProjectsTasks.configureEach { - shouldRunAfter(tasks.test) -} -testExternalProjectsTasks.configureEach { - shouldRunAfter(tasks.test) - shouldRunAfter(testTemplateProjectsTasks) -} - -// define lifecycle tasks for project tests -val testAllTemplateProjects by tasks.registering { - description = "Lifecycle task for running all template-project tests" - group = VERIFICATION_GROUP - dependsOn(testTemplateProjectsTasks) - doNotTrackState("lifecycle task, should always run") -} - -val testAllExternalProjects by tasks.registering { - description = "Lifecycle task for running all external-project tests" - group = VERIFICATION_GROUP - shouldRunAfter(testAllTemplateProjects) - dependsOn(testExternalProjectsTasks) - doNotTrackState("lifecycle task, should always run") -} - -tasks.integrationTest { - dependsOn(tasks.withType()) // all tests in this project are integration tests -} -//endregion - val checkoutBioJava by tasks.registering(GitCheckoutTask::class) { uri = "https://github.com/biojava/biojava.git" commitId = "059fbf1403d0704801df1427b0ec925102a645cd" From 4afd05c48b6085c7997fc6998cb2bfdef83b7d9c Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 5 Jun 2024 13:35:15 +0200 Subject: [PATCH 25/31] fix ENABLE_DEBUG env var --- .../src/main/kotlin/dokkabuild.test-integration.gradle.kts | 2 +- dokka-integration-tests/gradle/build.gradle.kts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts index 31a47e5654..a8173d74dd 100644 --- a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts @@ -16,7 +16,7 @@ plugins { val integrationTest by tasks.registering { description = "Lifecycle task for running all integration tests." - group = LifecycleBasePlugin.VERIFICATION_GROUP + group = VERIFICATION_GROUP } tasks.withType().configureEach { diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index 7b16972fd5..ffa6862c72 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -7,7 +7,6 @@ import dokkabuild.tasks.GitCheckoutTask import dokkabuild.utils.systemProperty import org.gradle.api.tasks.PathSensitivity.NAME_ONLY import org.gradle.api.tasks.PathSensitivity.RELATIVE -import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled plugins { @@ -64,7 +63,7 @@ kotlin { tasks.withType().configureEach { val enableDebug = providers.environmentVariable("ENABLE_DEBUG") inputs.property("enableDebug", enableDebug).optional(true) - environment("ENABLE_DEBUG", enableDebug.get()) + environment("ENABLE_DEBUG", enableDebug.getOrElse("false")) } val templateSettingsGradleKts = layout.projectDirectory.file("projects/template.settings.gradle.kts") From c3c1a62e710b261ce610388bfecc2fb40e03421c Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:05:17 +0200 Subject: [PATCH 26/31] update systemProperty util to be more consistent Refactor `fun MutableList.systemProperty()` to be more consistent with the other systemProperty utils, and so the intention is clearer. --- .../dokkabuild/DevMavenPublishExtension.kt | 5 +- .../dokkabuild/utils/SystemPropertyAdder.kt | 56 +++++++++++-------- 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt index 1728117e5d..a365fa6696 100644 --- a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt +++ b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt @@ -8,6 +8,7 @@ import org.gradle.api.Task import org.gradle.api.file.FileCollection import org.gradle.api.provider.Provider import org.gradle.api.tasks.PathSensitivity.RELATIVE +import org.gradle.api.tasks.testing.Test import org.gradle.process.JavaForkOptions import java.io.File @@ -48,8 +49,8 @@ abstract class DevMavenPublishExtension( task.dependsOn(devMavenRepositories) - if (task is JavaForkOptions) { - task.jvmArgumentProviders.systemProperty( + if (task is Test) { + task.systemProperty.internalProperty( "devMavenRepositories", devMavenRepositories.elements.map { paths -> paths.joinToString(",") { it.asFile.canonicalFile.invariantSeparatorsPath } diff --git a/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt index 484a0af95f..6cc924c4be 100644 --- a/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt +++ b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt @@ -14,7 +14,6 @@ import org.gradle.api.tasks.testing.Test import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.findByType import org.gradle.process.CommandLineArgumentProvider -import java.io.File import javax.inject.Inject @@ -100,8 +99,40 @@ abstract class SystemPropertyAdder @Inject internal constructor( key: String, value: Provider, ): TaskInputPropertyBuilder = inputProperty(key, value.map { it.toString() }) + + /** + * Add a System Property (in the format `-D$key=$value`). + * + * [value] will be treated as if it were annotated with [org.gradle.api.tasks.Internal] + * and will _not_ be registered as a Gradle [org.gradle.api.Task] input. + * (Which is beneficial in cases where [value] is optional, or is not reproducible, + * because such a property might disrupt task caching.) + * + * If you want to register the property as a Task input, use the + * [Test.systemProperty][dokkabuild.utils.systemProperty] above instead. + * + * @see org.gradle.api.tasks.Internal + */ + fun internalProperty( + key: String, + value: Provider, + ) { + task.jvmArgumentProviders.add( + SystemPropertyArgumentProvider( + key = key, + value = value, + transformer = { it.orNull }, + ) + ) + } } +/** + * Provide a Java system property. + * + * [value] is not registered as a Gradle Task input. + * The value must be registered as a task input, using the [SystemPropertyAdder] utils. + */ private class SystemPropertyArgumentProvider( @get:Input val key: String, @@ -113,26 +144,3 @@ private class SystemPropertyArgumentProvider( return listOf("-D$key=$value") } } - -/** - * Add a System Property (in the format `-D$key=$value`). - * - * [value] will _not_ be registered as a Gradle [org.gradle.api.Task] input. - * (Which might be beneficial in cases where the property is optional, or not reproducible, - * as such a property might disrupt task caching.) - * - * If you want to register the property as a Task input, use the - * [Test.systemProperty][dokkabuild.utils.systemProperty] above instead. - */ -fun MutableList.systemProperty( - key: String, - value: Provider, -) { - add( - SystemPropertyArgumentProvider( - key = key, - value = value, - transformer = { it.orNull }, - ) - ) -} From b887dc51444e781fbed469ce272347595d7446a6 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:06:48 +0200 Subject: [PATCH 27/31] remove unused import --- build-logic/src/main/kotlin/dokkabuild.base.gradle.kts | 1 - dokka-integration-tests/maven/build.gradle.kts | 1 - 2 files changed, 2 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild.base.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.base.gradle.kts index 6cb3a4b40c..70d3f48d86 100644 --- a/build-logic/src/main/kotlin/dokkabuild.base.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.base.gradle.kts @@ -2,7 +2,6 @@ * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ import dokkabuild.DokkaBuildProperties -import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP /** * A convention plugin that sets up common config and sensible defaults for all subprojects. diff --git a/dokka-integration-tests/maven/build.gradle.kts b/dokka-integration-tests/maven/build.gradle.kts index bb38950913..7be159bd65 100644 --- a/dokka-integration-tests/maven/build.gradle.kts +++ b/dokka-integration-tests/maven/build.gradle.kts @@ -7,7 +7,6 @@ import dokkabuild.tasks.GitCheckoutTask import dokkabuild.utils.systemProperty import org.gradle.api.tasks.PathSensitivity.NAME_ONLY import org.gradle.api.tasks.PathSensitivity.RELATIVE -import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP plugins { id("dokkabuild.kotlin-jvm") From 98668c198c8ef84cc70e6be033a58d86d4ae2a2a Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:20:02 +0200 Subject: [PATCH 28/31] move test suite dependency `implementation(project())` to convention --- .../src/main/kotlin/dokkabuild.test-integration.gradle.kts | 5 +++++ dokka-integration-tests/cli/build.gradle.kts | 6 ------ dokka-integration-tests/gradle/build.gradle.kts | 7 ------- dokka-integration-tests/maven/build.gradle.kts | 7 ------- 4 files changed, 5 insertions(+), 20 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts index a8173d74dd..2a2e95bece 100644 --- a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts @@ -71,6 +71,11 @@ testing { withType().configureEach { useJUnitJupiter() + dependencies { + // test suites are independent by default (unlike the test source set), and must manually depend on the project + implementation(project()) + } + targets.configureEach { testTask.configure { doFirst { diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 5fdf4b3e9d..48dcc7f674 100644 --- a/dokka-integration-tests/cli/build.gradle.kts +++ b/dokka-integration-tests/cli/build.gradle.kts @@ -42,12 +42,6 @@ dependencies { testing { suites { - withType().configureEach { - dependencies { - implementation(project()) - } - } - register("cliIntegrationTest") { targets.configureEach { testTask.configure { diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index ffa6862c72..bbfe4f7e36 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -69,13 +69,6 @@ tasks.withType().configureEach { val templateSettingsGradleKts = layout.projectDirectory.file("projects/template.settings.gradle.kts") val templateProjectsDir = layout.projectDirectory.dir("projects") -testing.suites.withType().configureEach { - dependencies { - // test suites are independent by default (unlike the test source set), and must manually depend on the project - implementation(project()) - } -} - // register a separate test suite for each 'template' project registerTestProjectSuite( "testTemplateProjectAndroid", diff --git a/dokka-integration-tests/maven/build.gradle.kts b/dokka-integration-tests/maven/build.gradle.kts index 7be159bd65..b6e2418ee5 100644 --- a/dokka-integration-tests/maven/build.gradle.kts +++ b/dokka-integration-tests/maven/build.gradle.kts @@ -55,13 +55,6 @@ tasks.withType().configureEach { .withPathSensitivity(NAME_ONLY) } -testing.suites.withType().configureEach { - dependencies { - // test suites are independent by default (unlike the test source set), and must manually depend on the project - implementation(project()) - } -} - registerTestProjectSuite("testTemplateProjectMaven", "it-maven") registerTestProjectSuite("testExternalProjectBioJava", "biojava/biojava") { targets.configureEach { From dd8fa2543527f59b8dbe26b7b7cf267a397bcbac Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:35:28 +0200 Subject: [PATCH 29/31] disable explicitApi in integration tests by default --- .../main/kotlin/dokkabuild.test-integration.gradle.kts | 9 +++++++++ .../jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt | 2 +- .../kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt | 3 +-- dokka-integration-tests/gradle/build.gradle.kts | 4 ---- 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts index 2a2e95bece..3dcf2c1259 100644 --- a/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts +++ b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts @@ -8,6 +8,8 @@ import org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL import org.gradle.api.tasks.testing.logging.TestLogEvent.FAILED import org.gradle.api.tasks.testing.logging.TestLogEvent.SKIPPED import org.gradle.language.base.plugins.LifecycleBasePlugin.VERIFICATION_GROUP +import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension plugins { id("dokkabuild.base") @@ -126,3 +128,10 @@ val testAllExternalProjects by tasks.registering { doNotTrackState("lifecycle task, should always run") } //endregion + +pluginManager.withPlugin("dokkabuild.kotlin-jvm") { + extensions.configure { + // integration test projects only contains test utils, and aren't published, so it doesn't matter about explicit API + explicitApi = Disabled + } +} diff --git a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt index fa284bc2cd..7b6708931a 100644 --- a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt +++ b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt @@ -7,7 +7,7 @@ import org.jetbrains.dokka.it.AbstractIntegrationTest import org.jetbrains.dokka.it.systemProperty import java.io.File -public abstract class AbstractCliIntegrationTest : AbstractIntegrationTest() { +abstract class AbstractCliIntegrationTest : AbstractIntegrationTest() { /** The Dokka CLI JAR. */ protected val dokkaCliJarPath: String by systemProperty { it.split(File.pathSeparatorChar) diff --git a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt index a14cb462ca..35e176b4cf 100644 --- a/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt +++ b/dokka-integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/jsonBuilder.kt @@ -1,13 +1,12 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ - package org.jetbrains.dokka.it.cli import org.intellij.lang.annotations.Language @Language("JSON") -public fun jsonBuilder( +fun jsonBuilder( outputPath: String, pluginsClasspath: String, projectPath: String, diff --git a/dokka-integration-tests/gradle/build.gradle.kts b/dokka-integration-tests/gradle/build.gradle.kts index bbfe4f7e36..e8b9773fef 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -7,7 +7,6 @@ import dokkabuild.tasks.GitCheckoutTask import dokkabuild.utils.systemProperty import org.gradle.api.tasks.PathSensitivity.NAME_ONLY import org.gradle.api.tasks.PathSensitivity.RELATIVE -import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled plugins { id("dokkabuild.kotlin-jvm") @@ -52,9 +51,6 @@ dependencies { } kotlin { - // this project only contains test utils and isn't published, so it doesn't matter about explicit API - explicitApi = Disabled - compilerOptions { optIn.add("kotlin.io.path.ExperimentalPathApi") } From b3d8d786da505bc9b51689052922d0f162028201 Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Tue, 18 Jun 2024 17:27:16 +0200 Subject: [PATCH 30/31] modify DevMavenPublishExtension#configureTask to configure Test task --- .../dokkabuild/DevMavenPublishExtension.kt | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt index a365fa6696..77f24112e3 100644 --- a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt +++ b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt @@ -4,7 +4,6 @@ package dokkabuild import dokkabuild.utils.systemProperty -import org.gradle.api.Task import org.gradle.api.file.FileCollection import org.gradle.api.provider.Provider import org.gradle.api.tasks.PathSensitivity.RELATIVE @@ -39,24 +38,21 @@ abstract class DevMavenPublishExtension( .map { files -> files.map { it.asFile }.sorted() } /** - * Configures [task] to register [devMavenRepositories] as a task input, + * Configures [Test] task to register [devMavenRepositories] as a task input, * and (if possible) adds `devMavenRepositories` as a [JavaForkOptions.systemProperty]. */ - fun configureTask(task: Task) { + fun configureTask(task: Test) { task.inputs.files(devMavenRepositoriesInputFiles) .withPropertyName("devMavenPublish.devMavenRepositoriesInputFiles") .withPathSensitivity(RELATIVE) task.dependsOn(devMavenRepositories) - - if (task is Test) { - task.systemProperty.internalProperty( - "devMavenRepositories", - devMavenRepositories.elements.map { paths -> - paths.joinToString(",") { it.asFile.canonicalFile.invariantSeparatorsPath } - } - ) - } + task.systemProperty.internalProperty( + "devMavenRepositories", + devMavenRepositories.elements.map { paths -> + paths.joinToString(",") { it.asFile.canonicalFile.invariantSeparatorsPath } + } + ) } companion object { From eb127e180c23cef3068ced1b492ca23d8a688eae Mon Sep 17 00:00:00 2001 From: Adam Semenenko <152864218+adam-enko@users.noreply.github.com> Date: Wed, 19 Jun 2024 11:05:31 +0200 Subject: [PATCH 31/31] update internalProperty kdoc --- .../src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt index 6cc924c4be..9592fe0584 100644 --- a/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt +++ b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt @@ -108,8 +108,8 @@ abstract class SystemPropertyAdder @Inject internal constructor( * (Which is beneficial in cases where [value] is optional, or is not reproducible, * because such a property might disrupt task caching.) * - * If you want to register the property as a Task input, use the - * [Test.systemProperty][dokkabuild.utils.systemProperty] above instead. + * If you want to register the property as a Task input that can be normalized, use one of + * the typed inputs above. * * @see org.gradle.api.tasks.Internal */