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 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/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts b/build-logic/src/main/kotlin/dokkabuild.test-integration.gradle.kts index 3a33ffc5a8..3dcf2c1259 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,137 @@ /* * Copyright 2014-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ +@file:Suppress("UnstableApiUsage") -import org.gradle.api.tasks.testing.logging.TestExceptionFormat -import org.gradle.api.tasks.testing.logging.TestLogEvent +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 +import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode.Disabled +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension plugins { - id("dokkabuild.kotlin-jvm") + id("dokkabuild.base") + `jvm-test-suite` } -val integrationTestSourceSet: SourceSet = sourceSets.create("integrationTest") { - compileClasspath += sourceSets.main.get().output - runtimeClasspath += sourceSets.main.get().output +val integrationTest by tasks.registering { + description = "Lifecycle task for running all integration tests." + group = VERIFICATION_GROUP } -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) { +tasks.withType().configureEach { + setForkEvery(1) maxHeapSize = "2G" - description = "Runs integration tests." - group = "verification" - testClassesDirs = integrationTestSourceSet.output.classesDirs - classpath = integrationTestSourceSet.runtimeClasspath - - useJUnitPlatform { - if (dokkaBuild.integrationTestUseK2.get()) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") + dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> + maxParallelForks = parallelism } - systemProperty("org.jetbrains.dokka.experimental.tryK2", dokkaBuild.integrationTestUseK2.get()) + systemProperty.inputProperty("dokkaVersion", provider { project.version.toString() }) + systemProperty.inputProperty("dokkaVersionOverride", dokkaBuild.integrationTestDokkaVersionOverride) + .optional(true) - dokkaBuild.integrationTestParallelism.orNull?.let { parallelism -> - maxParallelForks = parallelism + val useK2 = dokkaBuild.integrationTestUseK2 + systemProperty.inputProperty("org.jetbrains.dokka.experimental.tryK2", useK2) + .optional(true) + useJUnitPlatform { + if (useK2.get()) excludeTags("onlyDescriptors", "onlyDescriptorsMPP") } - environment("isExhaustive", dokkaBuild.integrationTestExhaustive.get()) + 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" } + ) testLogging { - exceptionFormat = TestExceptionFormat.FULL - events(TestLogEvent.SKIPPED, TestLogEvent.FAILED) + 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() + + 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}") + } + } + } + } + } +} + +integrationTest.configure { + dependsOn(testing.suites) +} + +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 + +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/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt index 1bc681c219..77f24112e3 100644 --- a/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt +++ b/build-logic/src/main/kotlin/dokkabuild/DevMavenPublishExtension.kt @@ -3,11 +3,13 @@ */ package dokkabuild -import org.gradle.api.Task +import dokkabuild.utils.systemProperty import org.gradle.api.file.FileCollection -import org.gradle.api.file.FileTree +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 abstract class DevMavenPublishExtension( /** @@ -21,36 +23,36 @@ 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 { + private val devMavenRepositoriesInputFiles: Provider> = + devMavenRepositories + // 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. // The Gradle Module Metadata contains the same information (and more), // so the Maven metadata is redundant. - exclude("**/maven-metadata*.xml") - } + .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.map { it.asFile }.sorted() } /** - * Configures [task] to register [devMavenRepositories] as a task input, - * and (if possible) adds `devMavenRepository` as a [JavaForkOptions.systemProperty]. + * 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 JavaForkOptions) { - task.doFirst("devMavenRepositories systemProperty") { - // workaround https://github.com/gradle/gradle/issues/24267 - task.systemProperty( - "devMavenRepositories", - devMavenRepositories.joinToString(",") { it.canonicalFile.invariantSeparatorsPath } - ) + task.systemProperty.internalProperty( + "devMavenRepositories", + devMavenRepositories.elements.map { paths -> + paths.joinToString(",") { it.asFile.canonicalFile.invariantSeparatorsPath } } - } + ) } companion object { 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..9592fe0584 --- /dev/null +++ b/build-logic/src/main/kotlin/dokkabuild/utils/SystemPropertyAdder.kt @@ -0,0 +1,146 @@ +/* + * 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 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?.asFile?.invariantSeparatorsPath + } + ) + return task.inputs.file(file) + .withPropertyName("SystemProperty input file $key") + } + + 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() }) + + /** + * 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 that can be normalized, use one of + * the typed inputs above. + * + * @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, + 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") + } +} diff --git a/dokka-integration-tests/cli/build.gradle.kts b/dokka-integration-tests/cli/build.gradle.kts index 11375f60c4..48dcc7f674 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,20 @@ 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) - } - ) + systemProperty + .inputFiles("dokkaCliJarPath", configurations.dokkaCliResolver) + .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..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 @@ -5,11 +5,18 @@ 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() { +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() + protected val dokkaPluginsClasspath: String by systemProperty { + it.replace(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 b2c4eee5ca..e8b9773fef 100644 --- a/dokka-integration-tests/gradle/build.gradle.kts +++ b/dokka-integration-tests/gradle/build.gradle.kts @@ -4,18 +4,14 @@ @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` - `java-test-fixtures` } dependencies { @@ -55,140 +51,79 @@ 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") } } -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 - } - - 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 } + val enableDebug = providers.environmentVariable("ENABLE_DEBUG") + inputs.property("enableDebug", enableDebug).optional(true) + environment("ENABLE_DEBUG", enableDebug.getOrElse("false")) } -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()) - } +val templateSettingsGradleKts = layout.projectDirectory.file("projects/template.settings.gradle.kts") +val templateProjectsDir = layout.projectDirectory.dir("projects") - 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 { + // 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. + environment("ANDROID_HOME", dokkaBuild.androidSdkDir.get().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. @@ -196,7 +131,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, @@ -204,82 +139,29 @@ 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() } } -//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 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) { uri = "https://github.com/Kotlin/kotlinx.coroutines.git" commitId = "b78bbf518bd8e90e9ed2133ebdacc36441210cd6" 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/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..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 @@ -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,7 @@ abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() { fun createGradleRunner( buildVersions: BuildVersions, vararg arguments: String, - jvmArgs: List = listOf("-Xmx2G", "-XX:MaxMetaspaceSize=1G") + jvmArgs: List = listOf("-Xmx2G", "-XX:MaxMetaspaceSize=1G"), ): GradleRunner { // TODO quick hack to add `android { namespace }` on AGP 7+ (it's mandatory in 8+). @@ -103,8 +104,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/maven/build.gradle.kts b/dokka-integration-tests/maven/build.gradle.kts index 4b2977b491..b6e2418ee5 100644 --- a/dokka-integration-tests/maven/build.gradle.kts +++ b/dokka-integration-tests/maven/build.gradle.kts @@ -1,20 +1,26 @@ /* * 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 dokkabuild.utils.systemProperty +import org.gradle.api.tasks.PathSensitivity.NAME_ONLY import org.gradle.api.tasks.PathSensitivity.RELATIVE plugins { - id("dokkabuild.test-integration") - id("dokkabuild.setup-maven-cli") + id("dokkabuild.kotlin-jvm") id("dokkabuild.dev-maven-publish") + id("dokkabuild.setup-maven-cli") + id("dokkabuild.test-integration") + `jvm-test-suite` } dependencies { - implementation(projects.utilities) + api(projects.utilities) - implementation(kotlin("test-junit5")) - implementation(libs.junit.jupiterApi) + api(libs.kotlin.test) + api(libs.junit.jupiterApi) val dokkaVersion = project.version.toString() // We're using Gradle included-builds and dependency substitution, so we @@ -43,25 +49,57 @@ dependencies { val templateProjectsDir = layout.projectDirectory.dir("projects") -val dokkaSubprojects = gradle.includedBuild("dokka") -val mavenPlugin = gradle.includedBuild("runner-maven-plugin") +tasks.withType().configureEach { + dependsOn(tasks.installMavenBinary) + systemProperty.inputFile("mavenBinaryFile", mavenCliSetup.mvn) + .withPathSensitivity(NAME_ONLY) +} -tasks.integrationTest { - dependsOn(checkoutBioJava) +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) + } + } +} - dependsOn(tasks.installMavenBinary) - val mvn = mavenCliSetup.mvn - inputs.file(mvn) +/** + * 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 registerTestProjectSuite( + name: String, + projectPath: String, + jvm: JavaLanguageVersion? = null, + configure: JvmTestSuite.() -> Unit = {}, +) { + val templateProjectDir = templateProjectsDir.dir(projectPath) - val dokkaVersion = provider { project.version.toString() } - inputs.property("dokkaVersion", dokkaVersion) + testing.suites.register(name) { + targets.configureEach { + testTask.configure { + // Pass the template dir in as a property, it is accessible in tests. + systemProperty + .inputDirectory("templateProjectDir", templateProjectDir) + .withPathSensitivity(RELATIVE) - doFirst("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) - devMavenPublish.configureTask(this) + if (jvm != null) { + javaLauncher = javaToolchains.launcherFor { languageVersion = jvm } + } + } + } + configure() + } } val checkoutBioJava by tasks.registering(GitCheckoutTask::class) { 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 92% 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..749ad98efb 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 @@ -13,8 +13,8 @@ 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 dokkaVersion: String by systemProperty() + 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 { @@ -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/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt b/dokka-integration-tests/maven/src/testTemplateProjectMaven/kotlin/MavenIntegrationTest.kt similarity index 96% 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..d4f52be203 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,14 +7,15 @@ 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.* class MavenIntegrationTest : AbstractIntegrationTest() { - private val currentDokkaVersion: String = checkNotNull(System.getenv("DOKKA_VERSION")) + private val dokkaVersion: String by systemProperty() - 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 { @@ -29,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..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 */ @@ -28,3 +28,24 @@ fun systemProperty( } convert(value) } + +/** + * Delegated accessor for an optional system property with the same name as the property. + * + * @see System.getProperty + */ +fun optionalSystemProperty(): ReadOnlyProperty = + optionalSystemProperty { it } + +/** + * Delegated accessor for an optional system property with the same name as the 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/gradle/libs.versions.toml b/gradle/libs.versions.toml index d9184b008a..18c7f581e7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -64,7 +64,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] 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 +}