From 8e5c63d035ef44a269b8c43430f43f5c8eebfb63 Mon Sep 17 00:00:00 2001 From: Ignat Beresnev Date: Fri, 10 Nov 2023 11:46:54 +0100 Subject: Restructure the project to utilize included builds (#3174) * Refactor and simplify artifact publishing * Update Gradle to 8.4 * Refactor and simplify convention plugins and build scripts Fixes #3132 --------- Co-authored-by: Adam <897017+aSemy@users.noreply.github.com> Co-authored-by: Oleg Yukhnevich --- .../dokka/analysis/kotlin/KotlinAnalysisPlugin.kt | 21 ++ .../internal/DocumentableSourceLanguageParser.kt | 20 ++ .../internal/ExternalDocumentablesProvider.kt | 28 +++ .../kotlin/internal/FullClassHierarchyBuilder.kt | 21 ++ .../analysis/kotlin/internal/InheritanceBuilder.kt | 25 +++ .../internal/InternalKotlinAnalysisPlugin.kt | 38 ++++ .../kotlin/internal/KotlinToJavaService.kt | 29 +++ .../ModuleAndPackageDocumentationReader.kt | 19 ++ .../analysis/kotlin/internal/SampleProvider.kt | 36 ++++ .../internal/SyntheticDocumentableDetector.kt | 15 ++ .../test/jvm/java/SampleJavaAnalysisTest.kt | 49 +++++ .../test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt | 43 ++++ .../test/jvm/mixed/SampleMixedJvmAnalysisTest.kt | 81 ++++++++ .../moduledocs/PackageDocumentationAnalysisTest.kt | 66 ++++++ .../analysis/test/sample/SampleAnalysisTest.kt | 55 +++++ .../jetbrains/dokka/analysis/test/api/TestData.kt | 21 ++ .../dokka/analysis/test/api/TestDataFile.kt | 37 ++++ .../dokka/analysis/test/api/TestProject.kt | 97 +++++++++ .../dokka/analysis/test/api/TestProjectFactory.kt | 67 +++++++ .../test/api/analysis/TestAnalysisContext.kt | 36 ++++ .../test/api/analysis/TestAnalysisServices.kt | 20 ++ .../test/api/analysis/TestProjectAnalyzer.kt | 223 +++++++++++++++++++++ .../api/configuration/TestDokkaConfiguration.kt | 171 ++++++++++++++++ .../configuration/TestDokkaConfigurationBuilder.kt | 145 ++++++++++++++ .../configuration/TestDokkaConfigurationMapper.kt | 177 ++++++++++++++++ .../test/api/jvm/java/JavaConfigurationBuilder.kt | 61 ++++++ .../analysis/test/api/jvm/java/JavaFileCreator.kt | 25 +++ .../analysis/test/api/jvm/java/JavaTestData.kt | 54 +++++ .../analysis/test/api/jvm/java/JavaTestDataFile.kt | 27 +++ .../analysis/test/api/jvm/java/JavaTestProject.kt | 73 +++++++ .../jvm/kotlin/KotlinJvmConfigurationBuilder.kt | 56 ++++++ .../api/jvm/kotlin/KotlinJvmDependencyUtils.kt | 22 ++ .../test/api/jvm/kotlin/KotlinJvmTestProject.kt | 93 +++++++++ .../api/jvm/mixed/MixedJvmConfigurationBuilder.kt | 69 +++++++ .../test/api/jvm/mixed/MixedJvmTestData.kt | 47 +++++ .../test/api/jvm/mixed/MixedJvmTestProject.kt | 80 ++++++++ .../analysis/test/api/kotlin/KotlinTestData.kt | 48 +++++ .../analysis/test/api/kotlin/KotlinTestDataFile.kt | 27 +++ .../analysis/test/api/kotlin/KtFileCreator.kt | 32 +++ .../api/kotlin/sample/KotlinSampleFileCreator.kt | 32 +++ .../test/api/kotlin/sample/KotlinSampleTestData.kt | 44 ++++ .../api/kotlin/sample/KotlinSampleTestDataFile.kt | 27 +++ .../analysis/test/api/markdown/MarkdownTestData.kt | 40 ++++ .../test/api/markdown/MarkdownTestDataFile.kt | 26 +++ .../analysis/test/api/markdown/MdFileCreator.kt | 28 +++ .../analysis/test/api/util/CollectionUtils.kt | 18 ++ .../dokka/analysis/test/api/util/DslApiUtils.kt | 9 + .../dokka/analysis/test/api/util/FileUtils.kt | 41 ++++ 48 files changed, 2519 insertions(+) create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/KotlinAnalysisPlugin.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/DocumentableSourceLanguageParser.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ExternalDocumentablesProvider.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/FullClassHierarchyBuilder.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InheritanceBuilder.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ModuleAndPackageDocumentationReader.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestData.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestDataFile.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProject.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProjectFactory.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisContext.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisServices.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestProjectAnalyzer.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfiguration.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfigurationBuilder.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfigurationMapper.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaConfigurationBuilder.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaFileCreator.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestData.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestDataFile.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestProject.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmConfigurationBuilder.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmDependencyUtils.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmTestProject.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmConfigurationBuilder.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestData.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestProject.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/KotlinTestData.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/KotlinTestDataFile.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/KtFileCreator.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/sample/KotlinSampleFileCreator.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/sample/KotlinSampleTestData.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/kotlin/sample/KotlinSampleTestDataFile.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/markdown/MarkdownTestData.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/markdown/MarkdownTestDataFile.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/markdown/MdFileCreator.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/CollectionUtils.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/DslApiUtils.kt create mode 100644 dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/FileUtils.kt (limited to 'dokka-subprojects/analysis-kotlin-api/src') diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/KotlinAnalysisPlugin.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/KotlinAnalysisPlugin.kt new file mode 100644 index 00000000..7d434bd5 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/KotlinAnalysisPlugin.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin + +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement + +public class KotlinAnalysisPlugin : DokkaPlugin() { + + /* + * This is where stable public API will go. + * + * No stable public API for now. + */ + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/DocumentableSourceLanguageParser.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/DocumentableSourceLanguageParser.kt new file mode 100644 index 00000000..116adb06 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/DocumentableSourceLanguageParser.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.WithSources + +@InternalDokkaApi +public enum class DocumentableLanguage { + JAVA, KOTLIN +} + +@InternalDokkaApi +public interface DocumentableSourceLanguageParser { + public fun getLanguage(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): DocumentableLanguage? +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ExternalDocumentablesProvider.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ExternalDocumentablesProvider.kt new file mode 100644 index 00000000..7c564880 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ExternalDocumentablesProvider.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.DClasslike + +/** + * Service that can be queried with [DRI] and source set to obtain a documentable for classlike. + * + * There are some cases when there is a need to process documentables of classlikes that were not defined + * in the project itself but are somehow related to the symbols defined in the documented project (e.g. are supertypes + * of classes defined in project). + */ +@InternalDokkaApi +public fun interface ExternalDocumentablesProvider { + + /** + * Returns [DClasslike] matching provided [DRI] in specified source set. + * + * Result is null if compiler haven't generated matching class descriptor. + */ + public fun findClasslike(dri: DRI, sourceSet: DokkaConfiguration.DokkaSourceSet): DClasslike? +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/FullClassHierarchyBuilder.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/FullClassHierarchyBuilder.kt new file mode 100644 index 00000000..5b975fd8 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/FullClassHierarchyBuilder.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.SourceSetDependent + +@InternalDokkaApi +public typealias Supertypes = List + +@InternalDokkaApi +public typealias ClassHierarchy = SourceSetDependent> + +@InternalDokkaApi +public interface FullClassHierarchyBuilder { + public suspend fun build(module: DModule): ClassHierarchy +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InheritanceBuilder.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InheritanceBuilder.kt new file mode 100644 index 00000000..32077cde --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InheritanceBuilder.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable + +@InternalDokkaApi +public interface InheritanceBuilder { + public fun build(documentables: Map): List +} + +@InternalDokkaApi +public data class InheritanceNode( + val dri: DRI, + val children: List = emptyList(), + val interfaces: List = emptyList(), + val isInterface: Boolean = false +) { + override fun equals(other: Any?): Boolean = other is InheritanceNode && other.dri == dri + override fun hashCode(): Int = dri.hashCode() +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt new file mode 100644 index 00000000..0ef1399a --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.ExtensionPoint +import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement + +/** + * A plugin for internal use, has no stable public API and thus must not be used by third party, + * external plugins. If you need any of the given API stabilized, please create an issue describing your use case. + */ +@InternalDokkaApi +public class InternalKotlinAnalysisPlugin : DokkaPlugin() { + + public val fullClassHierarchyBuilder: ExtensionPoint by extensionPoint() + + public val syntheticDocumentableDetector: ExtensionPoint by extensionPoint() + + public val moduleAndPackageDocumentationReader: ExtensionPoint by extensionPoint() + + public val kotlinToJavaService: ExtensionPoint by extensionPoint() + + public val inheritanceBuilder: ExtensionPoint by extensionPoint() + + public val externalDocumentablesProvider: ExtensionPoint by extensionPoint() + + public val documentableSourceLanguageParser: ExtensionPoint by extensionPoint() + + public val sampleProviderFactory: ExtensionPoint by extensionPoint() + + @OptIn(DokkaPluginApiPreview::class) + override fun pluginApiPreviewAcknowledgement(): PluginApiPreviewAcknowledgement = PluginApiPreviewAcknowledgement +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt new file mode 100644 index 00000000..1ce47031 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/KotlinToJavaService.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.links.DRI + +@InternalDokkaApi +public interface KotlinToJavaService { + /** + * E.g. + * kotlin.Throwable -> java.lang.Throwable + * kotlin.Int -> java.lang.Integer + * kotlin.Int.Companion -> kotlin.jvm.internal.IntCompanionObject + * kotlin.Nothing -> java.lang.Void + * kotlin.IntArray -> null + * kotlin.Function3 -> kotlin.jvm.functions.Function3 + * kotlin.coroutines.SuspendFunction3 -> kotlin.jvm.functions.Function4 + * kotlin.Function42 -> kotlin.jvm.functions.FunctionN + * kotlin.coroutines.SuspendFunction42 -> kotlin.jvm.functions.FunctionN + * kotlin.reflect.KFunction3 -> kotlin.reflect.KFunction + * kotlin.reflect.KSuspendFunction3 -> kotlin.reflect.KFunction + * kotlin.reflect.KFunction42 -> kotlin.reflect.KFunction + * kotlin.reflect.KSuspendFunction42 -> kotlin.reflect.KFunction + */ + public fun findAsJava(kotlinDri: DRI): DRI? +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ModuleAndPackageDocumentationReader.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ModuleAndPackageDocumentationReader.kt new file mode 100644 index 00000000..70419e0e --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/ModuleAndPackageDocumentationReader.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.DPackage +import org.jetbrains.dokka.model.SourceSetDependent +import org.jetbrains.dokka.model.doc.DocumentationNode + +@InternalDokkaApi +public interface ModuleAndPackageDocumentationReader { + public fun read(module: DModule): SourceSetDependent + public fun read(pkg: DPackage): SourceSetDependent + public fun read(module: DokkaConfiguration.DokkaModuleDescription): DocumentationNode? +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider.kt new file mode 100644 index 00000000..472d17f0 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi + +@InternalDokkaApi +public interface SampleProviderFactory { + /** + * [SampleProvider] is a short-lived closeable instance. + * It assumes that [SampleProvider] scope of use is not big. + * Otherwise, it can lead to high memory consumption / leaks during Dokka running. + */ + public fun build(): SampleProvider +} + +/** + * It is closeable. + * Otherwise, there is a chance of high memory consumption / leak. + * In general case, it creates a separate project to analysis samples directories. + */ +@InternalDokkaApi +public interface SampleProvider: AutoCloseable { + public class SampleSnippet( + public val imports: String, + public val body: String + ) + + /** + * @return [SampleSnippet] or null if it has not found by [fqLink] + */ + public fun getSample(sourceSet: DokkaConfiguration.DokkaSourceSet, fqLink: String): SampleSnippet? +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector.kt new file mode 100644 index 00000000..0a2d64de --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.kotlin.internal + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.InternalDokkaApi +import org.jetbrains.dokka.model.Documentable + +// TODO [beresnev] isSynthetic could be a property of Documentable +@InternalDokkaApi +public interface SyntheticDocumentableDetector { + public fun isSynthetic(documentable: Documentable, sourceSet: DokkaConfiguration.DokkaSourceSet): Boolean +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt new file mode 100644 index 00000000..f6632f60 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/java/SampleJavaAnalysisTest.kt @@ -0,0 +1,49 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.jvm.java + +import org.jetbrains.dokka.analysis.test.api.javaTestProject +import org.jetbrains.dokka.analysis.test.api.parse +import kotlin.test.Test +import kotlin.test.assertEquals + +class SampleJavaAnalysisTest { + + /** + * Used as a sample for [javaTestProject] + */ + @Test + fun sample() { + val testProject = javaTestProject { + dokkaConfiguration { + moduleName = "java-module-name-for-unit-test" + + javaSourceSet { + // source-set specific configuration + } + } + javaFile(pathFromSrc = "org/jetbrains/dokka/test/java/Bar.java") { + +""" + public class Bar { + public static void bar() { + System.out.println("Bar"); + } + } + """ + } + } + + val module = testProject.parse() + assertEquals("java-module-name-for-unit-test", module.name) + assertEquals(1, module.packages.size) + + val pckg = module.packages[0] + assertEquals("org.jetbrains.dokka.test.java", pckg.name) + assertEquals(1, pckg.classlikes.size) + + val fooClass = pckg.classlikes[0] + assertEquals("Bar", fooClass.name) + } +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt new file mode 100644 index 00000000..6c73af1f --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/kotlin/SampleKotlinJvmAnalysisTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.jvm.kotlin + +import org.jetbrains.dokka.analysis.test.api.kotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.parse +import kotlin.test.Test +import kotlin.test.assertEquals + +class SampleKotlinJvmAnalysisTest { + + /** + * Used as a sample for [kotlinJvmTestProject] + */ + @Test + fun sample() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + moduleName = "kotlin-jvm-module-name-for-unit-test" + + kotlinSourceSet { + // source-set specific configuration + } + } + ktFile(pathFromSrc = "org/jetbrains/dokka/test/kotlin/MyFile.kt") { + +"public class Foo {}" + } + } + + val module = testProject.parse() + assertEquals("kotlin-jvm-module-name-for-unit-test", module.name) + assertEquals(1, module.packages.size) + + val pckg = module.packages[0] + assertEquals("org.jetbrains.dokka.test.kotlin", pckg.name) + assertEquals(1, pckg.classlikes.size) + + val fooClass = pckg.classlikes[0] + assertEquals("Foo", fooClass.name) + } +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt new file mode 100644 index 00000000..fec2ceb8 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/jvm/mixed/SampleMixedJvmAnalysisTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.jvm.mixed + +import org.jetbrains.dokka.analysis.test.api.mixedJvmTestProject +import org.jetbrains.dokka.analysis.test.api.parse +import kotlin.test.Test +import kotlin.test.assertEquals + +class SampleMixedJvmAnalysisTest { + + /** + * Used as a sample for [mixedJvmTestProject] + */ + @Test + fun sample() { + val testProject = mixedJvmTestProject { + dokkaConfiguration { + moduleName = "mixed-project-module-name-for-unit-test" + + jvmSourceSet { + // source-set specific configuration + } + } + + kotlinSourceDirectory { + ktFile(pathFromSrc = "test/MyFile.kt") { + +"fun foo(): String = \"Foo\"" + } + javaFile(pathFromSrc = "test/MyJavaFileInKotlin.java") { + +""" + public class MyJavaFileInKotlin { + public static void bar() { + System.out.println("Bar"); + } + } + """ + } + } + + javaSourceDirectory { + ktFile(pathFromSrc = "test/MyFile.kt") { + +"fun bar(): String = \"Bar\"" + } + javaFile(pathFromSrc = "test/MyJavaFileInJava.java") { + +""" + public class MyJavaFileInJava { + public static void bar() { + System.out.println("Bar"); + } + } + """ + } + } + } + + val module = testProject.parse() + assertEquals("mixed-project-module-name-for-unit-test", module.name) + assertEquals(1, module.packages.size) + + val pckg = module.packages[0] + assertEquals("test", pckg.name) + + assertEquals(2, pckg.classlikes.size) + assertEquals(2, pckg.functions.size) + + val firstClasslike = pckg.classlikes[0] + assertEquals("MyJavaFileInKotlin", firstClasslike.name) + + val secondClasslike = pckg.classlikes[1] + assertEquals("MyJavaFileInJava", secondClasslike.name) + + val firstFunction = pckg.functions[0] + assertEquals("bar", firstFunction.name) + + val secondFunction = pckg.functions[1] + assertEquals("foo", secondFunction.name) + } +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt new file mode 100644 index 00000000..55507023 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/moduledocs/PackageDocumentationAnalysisTest.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.moduledocs + +import org.jetbrains.dokka.analysis.test.api.kotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.useServices +import org.jetbrains.dokka.model.doc.CodeInline +import org.jetbrains.dokka.model.doc.Description +import org.jetbrains.dokka.model.doc.P +import org.jetbrains.dokka.model.doc.Text +import kotlin.test.Test +import kotlin.test.assertEquals + +class PackageDocumentationAnalysisTest { + + @Test + fun `should parse include description for a nested package in kotlin-jvm`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + includes = setOf("/documentation/cool-package-description.md") + } + } + + ktFile(pathFromSrc = "org/jetbrains/dokka/pckg/docs/test/TestFile.kt") { + +"class TestFile {}" + } + + mdFile(pathFromProjectRoot = "/documentation/cool-package-description.md") { + +""" + # Package org.jetbrains.dokka.pckg.docs.test + + This is my test description for the package `org.jetbrains.dokka.pckg.docs.test`, + which contains only one file named TestFile.kt. It has one empty class. + """ + } + } + + testProject.useServices { context -> + val pckg = context.module.packages.single { it.name == "org.jetbrains.dokka.pckg.docs.test" } + + val allPackageDocs = moduleAndPackageDocumentationReader.read(pckg) + assertEquals(1, allPackageDocs.size) + + val sourceSetPackageDocs = allPackageDocs.entries.single().value + assertEquals(1, sourceSetPackageDocs.children.size) + + val descriptionTag = sourceSetPackageDocs.children.single() as Description + assertEquals(1, descriptionTag.children.size) + + val paragraphTag = descriptionTag.children.single() as P + assertEquals(3, paragraphTag.children.size) + + val expectedParagraphChildren = listOf( + Text("This is my test description for the package "), + CodeInline(children = listOf(Text( + "org.jetbrains.dokka.pckg.docs.test" + ))), + Text(", which contains only one file named TestFile.kt. It has one empty class.") + ) + assertEquals(expectedParagraphChildren, paragraphTag.children) + } + } +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt new file mode 100644 index 00000000..618e28a8 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.sample + +import org.jetbrains.dokka.analysis.test.api.kotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.useServices +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class SampleAnalysisTest { + + @Test + fun `should return sources of a kotlin sample`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + additionalSourceRoots = setOf("/samples") + } + } + sampleFile("/samples/stringListOf-sample.kt", fqPackageName = "org.jetbrains.dokka.sample.generator") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + import org.jetbrains.dokka.DokkaGenerator + import org.jetbrains.dokka.utilities.DokkaLogger + + fun runGenerator(configuration: DokkaConfiguration, logger: DokkaLogger) { + DokkaGenerator(configuration, logger).generate() + } + """ + } + } + + testProject.useServices { context -> + val sampleSourceSet = context.configuration.sourceSets.single() + + val sampleProvider = sampleProviderFactory.build() + val sample = sampleProvider.getSample(sampleSourceSet, "org.jetbrains.dokka.sample.generator.runGenerator") + assertNotNull(sample) + + val expectedImports = listOf( + "import org.jetbrains.dokka.DokkaConfiguration", + "import org.jetbrains.dokka.DokkaGenerator", + "import org.jetbrains.dokka.utilities.DokkaLogger" + ).joinToString(separator = "\n") + + val expectedBody = "DokkaGenerator(configuration, logger).generate()" + + assertEquals(expectedImports, sample.imports) + assertEquals(expectedBody, sample.body) + } + } +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestData.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestData.kt new file mode 100644 index 00000000..64bfd7a3 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestData.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api + +import org.jetbrains.dokka.analysis.test.api.util.AnalysisTestDslMarker + +/** + * Represents some sort of data of a [TestProject], which normally consists of a number of [TestDataFile]. + * + * This can be anything that can usually be found in a user-defined project: + * programming language source code, markdown files with documentation, samples, etc. + * + * This virtual test data will be materialized and created physically before running Dokka, + * and then passed as input files into it. + */ +@AnalysisTestDslMarker +interface TestData { + fun getFiles(): List +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestDataFile.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestDataFile.kt new file mode 100644 index 00000000..5b2233ba --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestDataFile.kt @@ -0,0 +1,37 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api + +import org.jetbrains.dokka.analysis.test.api.util.AnalysisTestDslMarker + +/** + * Represents a single file of a project's [TestData]. + * + * This file will be materialized and created physically before running Dokka, + * and then passed as one of the input files into it. + * + * @property pathFromProjectRoot this file's path from the root of the project. Must begin + * with `/` to not confuse it with relative paths. + */ +@AnalysisTestDslMarker +abstract class TestDataFile(val pathFromProjectRoot: String) { + + init { + require(pathFromProjectRoot.startsWith("/")) { + "File path going from the project's root must begin with \"/\" to not confuse it with relative paths." + } + } + + /** + * Returns the string contents of this file. + * + * The contents must be complete, as if the user themselves wrote it. For Kotlin files, + * it should return Kotlin source code (including the package and all import statements). + * For `.md` files, it should return valid Markdown documentation. + * + * These contents will be used to populate the real input file to be used by Dokka. + */ + abstract fun getContents(): String +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProject.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProject.kt new file mode 100644 index 00000000..9c0fa936 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProject.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.test.api.analysis.TestAnalysisContext +import org.jetbrains.dokka.analysis.test.api.analysis.TestAnalysisServices +import org.jetbrains.dokka.analysis.test.api.analysis.TestProjectAnalyzer +import org.jetbrains.dokka.analysis.test.api.configuration.BaseTestDokkaConfigurationBuilder +import org.jetbrains.dokka.analysis.test.api.configuration.TestDokkaConfiguration +import org.jetbrains.dokka.analysis.test.api.util.withTempDirectory +import org.jetbrains.dokka.model.DModule + +/** + * Represents a virtual test project (as if it's user-defined) that will be used to run Dokka. + * + * A test project consists of some Dokka configuration (represented as [TestDokkaConfiguration]) + * and some project-specific data like source code and markdown files (represented as [TestData]). + * + * See [kotlinJvmTestProject], [javaTestProject] and [mixedJvmTestProject] for convenient ways + * of bootstrapping test projects. + * + * See [parse] and [useServices] functions to learn how to run Dokka with this project as input. + */ +interface TestProject { + + /** + * Verifies that this project is valid from the user's and Dokka's perspectives. + * Exists to save time with debugging difficult to catch mistakes, such as copy-pasted + * test data that is not applicable to this project. + * + * Must throw an exception if there's misconfiguration, incorrect / corrupted test data + * or API misuse. + * + * Verification is performed before running Dokka on this project. + */ + fun verify() + + /** + * Returns the configuration of this project, which will then be mapped to [DokkaConfiguration]. + * + * This is typically constructed using [BaseTestDokkaConfigurationBuilder]. + */ + fun getConfiguration(): TestDokkaConfiguration + + /** + * Returns this project's test data - a collection of source code files, markdown files + * and whatever else that can be usually found in a user-defined project. + */ + fun getTestData(): TestData +} + +/** + * Runs Dokka on the given [TestProject] and returns the generated documentable model. + * + * Can be used to verify the resulting documentable model, to check that + * everything was parsed and converted correctly. + * + * Usage example: + * ```kotlin + * val testProject = kotlinJvmTestProject { + * ... + * } + * + * val module: DModule = testProject.parse() + * ``` + */ +fun TestProject.parse(): DModule = TestProjectAnalyzer.parse(this) + +/** + * Runs Dokka on the given [TestProject] and provides not only the resulting documentable model, + * but analysis context and configuration as well, which gives you the ability to call public + * analysis services. + * + * Usage example: + * + * ```kotlin + * val testProject = kotlinJvmTestProject { + * ... + * } + * + * testProject.useServices { context -> + * val pckg: DPackage = context.module.packages.single() + * + * // use `moduleAndPackageDocumentationReader` service to get documentation of a package + * val allPackageDocs: SourceSetDependent = moduleAndPackageDocumentationReader.read(pckg) + * } + * ``` + */ +fun TestProject.useServices(block: TestAnalysisServices.(context: TestAnalysisContext) -> Unit) { + withTempDirectory { tempDirectory -> + val (services, context) = TestProjectAnalyzer.analyze(this, tempDirectory) + services.block(context) + } +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProjectFactory.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProjectFactory.kt new file mode 100644 index 00000000..81a20243 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProjectFactory.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api + +import org.jetbrains.dokka.analysis.test.api.jvm.java.JavaTestProject +import org.jetbrains.dokka.analysis.test.api.jvm.kotlin.KotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.jvm.mixed.MixedJvmTestProject +import org.jetbrains.dokka.analysis.test.api.util.AnalysisTestDslMarker + +/** + * Creates a single-target Kotlin/JVM test project that only has Kotlin source code. + * + * See [javaTestProject] and [mixedJvmTestProject] if you want to check interoperability + * with other JVM languages. + * + * By default, the sources are put in `/src/main/kotlin`, and the JVM version of Kotlin's + * standard library is available on classpath. + * + * See [parse] and [useServices] functions to learn how to run Dokka with this project as input. + * + * @sample org.jetbrains.dokka.analysis.test.jvm.kotlin.SampleKotlinJvmAnalysisTest.sample + */ +fun kotlinJvmTestProject(init: (@AnalysisTestDslMarker KotlinJvmTestProject).() -> Unit): TestProject { + val testData = KotlinJvmTestProject() + testData.init() + return testData +} + +/** + * Creates a Java-only test project. + * + * This can be used to test Dokka's Java support or specific + * corner cases related to parsing Java sources. + * + * By default, the sources are put in `/src/main/java`. No Kotlin source code is allowed. + * + * See [parse] and [useServices] functions to learn how to run Dokka with this project as input. + * + * @sample org.jetbrains.dokka.analysis.test.jvm.java.SampleJavaAnalysisTest.sample + */ +fun javaTestProject(init: (@AnalysisTestDslMarker JavaTestProject).() -> Unit): TestProject { + val testData = JavaTestProject() + testData.init() + return testData +} + +/** + * Creates a project where a number of JVM language sources are allowed, + * like Java and Kotlin sources co-existing in the same source directory. + * + * This can be used to test interoperability between JVM languages. + * + * By default, this project consists of a single "jvm" source set, which has two source root directories: + * * `/src/main/kotlin` + * * `/src/main/java` + * + * See [parse] and [useServices] functions to learn how to run Dokka with this project as input. + * + * @sample org.jetbrains.dokka.analysis.test.jvm.mixed.SampleMixedJvmAnalysisTest.sample + */ +fun mixedJvmTestProject(init: (@AnalysisTestDslMarker MixedJvmTestProject).() -> Unit): TestProject { + val testProject = MixedJvmTestProject() + testProject.init() + return testProject +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisContext.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisContext.kt new file mode 100644 index 00000000..de6efb1b --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisContext.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api.analysis + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.test.api.TestProject +import org.jetbrains.dokka.analysis.test.api.configuration.TestDokkaConfiguration +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +/** + * Context and data gathered during the analysis of a [TestProject]. + */ +class TestAnalysisContext( + + /** + * The actual [DokkaContext] that was used to run Dokka. + * + * Includes all plugins and classes available on classpath during the analysis. + */ + val context: DokkaContext, + + /** + * The actual [DokkaConfiguration] that was used to run Dokka. + * + * It was initially mapped from [TestDokkaConfiguration], and then added to by Dokka itself. + */ + val configuration: DokkaConfiguration, + + /** + * The entry point to the documentable model of the analyzed [TestProject]. + */ + val module: DModule +) diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisServices.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisServices.kt new file mode 100644 index 00000000..ab70bbd4 --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisServices.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api.analysis + +import org.jetbrains.dokka.analysis.kotlin.KotlinAnalysisPlugin +import org.jetbrains.dokka.analysis.kotlin.internal.ModuleAndPackageDocumentationReader +import org.jetbrains.dokka.analysis.kotlin.internal.SampleProviderFactory + +/** + * Services exposed in [KotlinAnalysisPlugin] that are ready to be used. + * + * This class exists purely for convenience and to reduce boilerplate in tests. + * It is analogous to calling `context.plugin().querySingle { serviceName }`. + */ +class TestAnalysisServices( + val sampleProviderFactory: SampleProviderFactory, + val moduleAndPackageDocumentationReader: ModuleAndPackageDocumentationReader +) diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestProjectAnalyzer.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestProjectAnalyzer.kt new file mode 100644 index 00000000..1668b53f --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestProjectAnalyzer.kt @@ -0,0 +1,223 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api.analysis + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin +import org.jetbrains.dokka.analysis.test.api.TestDataFile +import org.jetbrains.dokka.analysis.test.api.TestProject +import org.jetbrains.dokka.analysis.test.api.configuration.toDokkaConfiguration +import org.jetbrains.dokka.analysis.test.api.parse +import org.jetbrains.dokka.analysis.test.api.useServices +import org.jetbrains.dokka.analysis.test.api.util.withTempDirectory +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.transformers.documentation.DefaultDocumentableMerger +import org.jetbrains.dokka.transformers.documentation.DocumentableMerger +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.jetbrains.dokka.utilities.LoggingLevel +import java.io.File + +/** + * The main logger used for running Dokka and analyzing projects. + * + * Changing the level to [LoggingLevel.DEBUG] can help with debugging faulty tests + * or tricky corner cases. + */ +val analysisLogger = DokkaConsoleLogger(minLevel = LoggingLevel.INFO) + +/** + * Analyzer of the test projects, it is essentially a very simple Dokka runner. + * + * Takes all virtual files of the given [TestProject], creates the real files for + * them in a temporary directory, and then runs Dokka with this temporary directory + * as the input user project. This allows us to simulate Dokka's behavior and results + * on a made-up project as if it were real and run via the CLI runner. + * + * Executes only a limited number of steps and uses a small subset of [CoreExtensions] + * that are necessary to test the analysis logic. + * + * Works only with single-module projects, where the source code of this project + * resides in the root `src` directory. Works with multiple source sets and targets, + * so both simple Kotlin/JVM and more complicated Kotlin Multiplatform project must work. + */ +object TestProjectAnalyzer { + + /** + * A quick way to analyze a [TestProject], for cases when only the documentable + * model is needed to verify the result. + * + * Creates the input test files, runs Dokka and then deletes them right after the documentable + * model has been created, leaving no trailing files or any other garbage behind. + * + * @see [TestProject.parse] for a user-friendly way to call it + */ + fun parse(testProject: TestProject): DModule { + // since we only need documentables, we can delete the input test files right away + return withTempDirectory(analysisLogger) { tempDirectory -> + val (_, context) = testProject.initialize(outputDirectory = tempDirectory) + generateDocumentableModel(context) + } + } + + /** + * Works in the same way as [parse], but it returns the context and configuration used for + * running Dokka, and does not delete the input test files at the end of the execution - it + * must be taken care of on call site. + * + * @param persistentDirectory a directory that will be used to generate the input test files into. + * It must be available during the test run, especially if services are used, + * otherwise parts of Dokka might not work as expected. Can be safely deleted + * at the end of the test after all asserts have been run. + * + * @see [TestProject.useServices] for a user-friendly way to call it + */ + fun analyze( + testProject: TestProject, + persistentDirectory: File + ): Pair { + val (dokkaConfiguration, dokkaContext) = testProject.initialize(outputDirectory = persistentDirectory) + val analysisServices = createTestAnalysisServices(dokkaContext) + val testAnalysisContext = TestAnalysisContext( + context = dokkaContext, + configuration = dokkaConfiguration, + module = generateDocumentableModel(dokkaContext) + ) + return analysisServices to testAnalysisContext + } + + /** + * Prepares this [TestProject] for analysis by creating + * the test files, setting up context and configuration. + */ + private fun TestProject.initialize(outputDirectory: File): Pair { + analysisLogger.progress("Initializing and verifying project $this") + this.verify() + require(outputDirectory.isDirectory) { + "outputDirectory has to exist and be a directory: $outputDirectory" + } + this.initializeTestFiles(relativeToDir = outputDirectory) + + analysisLogger.progress("Creating configuration and context") + val testDokkaConfiguration = this.getConfiguration() + val dokkaConfiguration = testDokkaConfiguration.toDokkaConfiguration(projectDir = outputDirectory).also { + it.verify() + } + return dokkaConfiguration to createContext(dokkaConfiguration) + } + + /** + * Takes the virtual [TestDataFile] of this [TestProject] and creates + * the real files relative to the [relativeToDir] param. + */ + private fun TestProject.initializeTestFiles(relativeToDir: File) { + analysisLogger.progress("Initializing test files relative to the \"$relativeToDir\" directory") + + this.getTestData().getFiles().forEach { + val testDataFile = relativeToDir.resolve(it.pathFromProjectRoot.removePrefix("/")) + try { + testDataFile.parentFile.mkdirs() + } catch (e: Exception) { + // the IOException thrown from `mkdirs()` has no details and thus is more difficult to debug. + throw IllegalStateException("Unable to create dirs \"${testDataFile.parentFile}\"", e) + } + + analysisLogger.debug("Creating \"${testDataFile.absolutePath}\"") + check(testDataFile.createNewFile()) { + "Unable to create a test file: ${testDataFile.absolutePath}" + } + testDataFile.writeText(it.getContents(), Charsets.UTF_8) + } + } + + /** + * Verifies this [DokkaConfiguration] to make sure there are no unexpected + * parameter option values, such as non-existing classpath entries. + * + * If this method fails, it's likely there's a configuration error in the test, + * or an exception must be made in one of the checks. + */ + private fun DokkaConfiguration.verify() { + this.includes.forEach { verifyFileExists(it) } + this.sourceSets.forEach { sourceSet -> + sourceSet.classpath.forEach { verifyFileExists(it) } + sourceSet.includes.forEach { verifyFileExists(it) } + sourceSet.samples.forEach { verifyFileExists(it) } + // we do not verify sourceRoots since the source directory + // is not guaranteed to exist even if it was configured. + } + } + + private fun verifyFileExists(file: File) { + if (!file.exists() && !file.absolutePath.contains("non-existing")) { + throw IllegalArgumentException( + "The provided file does not exist. Bad test data or configuration? " + + "If it is done intentionally, add \"non-existing\" to the path or the name. File: \"$file\"" + ) + } + } + + private fun createContext(dokkaConfiguration: DokkaConfiguration): DokkaContext { + analysisLogger.progress("Creating DokkaContext from test configuration") + return DokkaContext.create( + configuration = dokkaConfiguration, + logger = analysisLogger, + pluginOverrides = listOf() + ) + } + + /** + * Generates the documentable model by using all available [SourceToDocumentableTranslator] extensions, + * and then merging all the results into a single [DModule] by calling [DocumentableMerger]. + */ + private fun generateDocumentableModel(context: DokkaContext): DModule { + analysisLogger.progress("Generating the documentable model") + val sourceSetModules = context + .configuration + .sourceSets + .map { sourceSet -> translateSources(sourceSet, context) } + .flatten() + + if (sourceSetModules.isEmpty()) { + throw IllegalStateException("Got no modules after translating sources. Is the test data set up?") + } + + return DefaultDocumentableMerger(context).invoke(sourceSetModules) + ?: error("Unable to merge documentables for some reason") + } + + /** + * Translates input source files to the documentable model by using + * all registered [SourceToDocumentableTranslator] core extensions. + */ + private fun translateSources(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): List { + val translators = context[CoreExtensions.sourceToDocumentableTranslator] + require(translators.isNotEmpty()) { + "Need at least one source to documentable translator to run tests, otherwise no data will be generated." + } + analysisLogger.debug("Translating sources for ${sourceSet.sourceSetID}") + return translators.map { it.invoke(sourceSet, context) } + } + + /** + * A helper function to query analysis services, to avoid + * boilerplate and misconfiguration in the tests. + * + * The idea is to provide the users with ready-to-use services, + * without them having to know how to query or configure them. + */ + private fun createTestAnalysisServices(context: DokkaContext): TestAnalysisServices { + analysisLogger.progress("Creating analysis services") + val internalPlugin = context.plugin() + return TestAnalysisServices( + sampleProviderFactory = internalPlugin.querySingle { sampleProviderFactory }, + moduleAndPackageDocumentationReader = internalPlugin.querySingle { moduleAndPackageDocumentationReader } + ) + } +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfiguration.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfiguration.kt new file mode 100644 index 00000000..5c5a0daf --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/configuration/TestDokkaConfiguration.kt @@ -0,0 +1,171 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.analysis.test.api.configuration + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.test.api.TestProject + +/** + * Configuration options for [TestProject]. + * + * Represents a trimmed-down version of [DokkaConfiguration] that only + * exposes properties that can be used by Dokka's analysis implementations. + */ +data class TestDokkaConfiguration( + + /** + * Name of this [TestProject]. + * + * @see DokkaConfiguration.moduleName + */ + val moduleName: String, + + /** + * References Markdown files that contain documentation for this module and packages. + * + * Contains paths relative to the root of [TestProject], so it must begin with `/`. + * + * Example: `/docs/module.md` + * + * @see DokkaConfiguration.includes + * @see https://kotlinlang.org/docs/dokka-module-and-package-docs.html + */ + val includes: Set = emptySet(), + + /** + * A number of source directories and their configuration options that + * make up this project. + * + * A multiplatform Kotlin project will typically have multiple source sets + * for the supported platforms, whereas a single Kotlin/JVM project will have only one. + * + * @see TestDokkaSourceSet + */ + val sourceSets: Set + + +) { + override fun toString(): String { + return "TestDokkaConfiguration(moduleName='$moduleName', includes=$includes, sourceSets=$sourceSets)" + } +} + +/** + * Configuration options for a collection of source files for a specific platform. + * + * Represents a trimmed-down version of [DokkaConfiguration.DokkaSourceSet] that only + * exposes properties that can be used by Dokka's analysis implementations. + * + * @see https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets + */ +data class TestDokkaSourceSet ( + + /** + * @see DokkaConfiguration.DokkaSourceSet.analysisPlatform + */ + val analysisPlatform: Platform, + + /** + * A unique identifier of this source set in the scope of the project. + * + * It must be unique even if two source sets have the same name, + * the same platform or the same configuration. + * + * @see DokkaConfiguration.DokkaSourceSet.sourceSetID + */ + val sourceSetID: DokkaSourceSetID, + + /** + * A set of source set ids that this source set depends on.