diff options
| author | Ignat Beresnev <ignat.beresnev@jetbrains.com> | 2023-11-21 11:28:45 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-21 11:28:45 +0100 |
| commit | 6fbc2221ff309995c605161b51d4d64cbabddd51 (patch) | |
| tree | a6476838442e47b3bbc03d753c74c84f58f67b1e /dokka-subprojects/analysis-kotlin-api | |
| parent | 9ce37affaa2c1199807c08e13485740ea993994e (diff) | |
| download | dokka-6fbc2221ff309995c605161b51d4d64cbabddd51.tar.gz dokka-6fbc2221ff309995c605161b51d4d64cbabddd51.tar.bz2 dokka-6fbc2221ff309995c605161b51d4d64cbabddd51.zip | |
Stabilize Sample analysis API (#3195)
Diffstat (limited to 'dokka-subprojects/analysis-kotlin-api')
18 files changed, 807 insertions, 116 deletions
diff --git a/dokka-subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api b/dokka-subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api index c65dfe5a..3b546932 100644 --- a/dokka-subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api +++ b/dokka-subprojects/analysis-kotlin-api/api/analysis-kotlin-api.api @@ -1,5 +1,6 @@ public final class org/jetbrains/dokka/analysis/kotlin/KotlinAnalysisPlugin : org/jetbrains/dokka/plugability/DokkaPlugin { public fun <init> ()V + public final fun getSampleAnalysisEnvironmentCreator ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; } public final class org/jetbrains/dokka/analysis/kotlin/internal/DocumentableLanguage : java/lang/Enum { @@ -51,7 +52,6 @@ public final class org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAn public final fun getInheritanceBuilder ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getKotlinToJavaService ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getModuleAndPackageDocumentationReader ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; - public final fun getSampleProviderFactory ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; public final fun getSyntheticDocumentableDetector ()Lorg/jetbrains/dokka/plugability/ExtensionPoint; } @@ -65,21 +65,24 @@ public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/Mod public abstract fun read (Lorg/jetbrains/dokka/model/DPackage;)Ljava/util/Map; } -public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider : java/lang/AutoCloseable { - public abstract fun getSample (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/lang/String;)Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider$SampleSnippet; +public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector { + public abstract fun isSynthetic (Lorg/jetbrains/dokka/model/Documentable;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Z } -public final class org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider$SampleSnippet { - public fun <init> (Ljava/lang/String;Ljava/lang/String;)V - public final fun getBody ()Ljava/lang/String; - public final fun getImports ()Ljava/lang/String; +public abstract interface class org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironment { + public abstract fun resolveSample (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;Ljava/lang/String;)Lorg/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet; } -public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SampleProviderFactory { - public abstract fun build ()Lorg/jetbrains/dokka/analysis/kotlin/internal/SampleProvider; +public abstract interface class org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironmentCreator { + public abstract fun use (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } -public abstract interface class org/jetbrains/dokka/analysis/kotlin/internal/SyntheticDocumentableDetector { - public abstract fun isSynthetic (Lorg/jetbrains/dokka/model/Documentable;Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Z +public final class org/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet { + public fun <init> (Ljava/util/List;Ljava/lang/String;)V + public fun equals (Ljava/lang/Object;)Z + public final fun getBody ()Ljava/lang/String; + public final fun getImports ()Ljava/util/List; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; } 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 index 7d434bd5..1df1dfe6 100644 --- 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 @@ -4,17 +4,21 @@ package org.jetbrains.dokka.analysis.kotlin +import org.jetbrains.dokka.analysis.kotlin.sample.SampleAnalysisEnvironmentCreator +import org.jetbrains.dokka.analysis.kotlin.sample.SampleAnalysisEnvironment import org.jetbrains.dokka.plugability.DokkaPlugin import org.jetbrains.dokka.plugability.DokkaPluginApiPreview +import org.jetbrains.dokka.plugability.ExtensionPoint import org.jetbrains.dokka.plugability.PluginApiPreviewAcknowledgement public class KotlinAnalysisPlugin : DokkaPlugin() { - /* - * This is where stable public API will go. + /** + * An extension for analyzing Kotlin sample functions used in the `@sample` KDoc tag. * - * No stable public API for now. + * @see SampleAnalysisEnvironment for more details */ + public val sampleAnalysisEnvironmentCreator: ExtensionPoint<SampleAnalysisEnvironmentCreator> 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/InternalKotlinAnalysisPlugin.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt index 0ef1399a..d032d490 100644 --- 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 @@ -31,8 +31,6 @@ public class InternalKotlinAnalysisPlugin : DokkaPlugin() { public val documentableSourceLanguageParser: ExtensionPoint<DocumentableSourceLanguageParser> by extensionPoint() - public val sampleProviderFactory: ExtensionPoint<SampleProviderFactory> 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/SampleProvider.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider.kt deleted file mode 100644 index 472d17f0..00000000 --- a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * 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/sample/SampleAnalysisEnvironment.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironment.kt new file mode 100644 index 00000000..3620808a --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironment.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.kotlin.sample + +import org.jetbrains.dokka.DokkaConfiguration + +/** + * Fully-configured and ready-to-use sample analysis environment. + * + * It's best to limit the scope of use and lifetime of this environment as it takes up + * additional resources which could be freed once the samples have been analyzed. + * Therefore, it's best to use it through the [SampleAnalysisEnvironmentCreator.use] lambda. + * + * For example, if you need to process all samples in an arbitrary project, it's best to do it + * in one iteration and at the same time, so that the environment is created once and lives for + * as little is possible, as opposed to creating it again and again for every individual sample. + */ +public interface SampleAnalysisEnvironment { + + /** + * Resolves a Kotlin sample function by its fully qualified name, and returns its import statements and body. + * + * @param sourceSet must be either the source set in which this sample function resides, or the source set + * for which [DokkaConfiguration#samples] or [DokkaConfiguration#sourceRoots] + * have been configured with the sample's sources. + * @param fullyQualifiedLink fully qualified path to the sample function, including all middle packages + * and the name of the function. Only links to Kotlin functions are valid, + * which can reside within a class. The package must be the same as the package + * declared in the sample file. The function must be resolvable by Dokka, + * meaning it must reside either in the main sources of the project or its + * sources must be included in [DokkaConfiguration#samples] or + * [DokkaConfiguration#sourceRoots]. Example: `com.example.pckg.topLevelKotlinFunction` + * + * @return a sample code snippet which includes import statements and the function body, + * or null if the link could not be resolved (examine the logs to find out the reason). + */ + public fun resolveSample( + sourceSet: DokkaConfiguration.DokkaSourceSet, + fullyQualifiedLink: String + ): SampleSnippet? +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironmentCreator.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironmentCreator.kt new file mode 100644 index 00000000..d64734ef --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironmentCreator.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.sample + +import org.jetbrains.dokka.analysis.kotlin.KotlinAnalysisPlugin + +/** + * Entry point to analyzing Kotlin samples. + * + * Can be acquired via [KotlinAnalysisPlugin.sampleAnalysisEnvironmentCreator]. + */ +public interface SampleAnalysisEnvironmentCreator { + + /** + * Creates and configures the sample analysis environment for a limited-time use. + * + * Configuring sample analysis environment is a rather expensive operation that takes up additional + * resources since Dokka needs to configure and analyze source roots additional to the main ones. + * It's best to limit the scope of use and the lifetime of the created environment + * so that the resources could be freed as soon as possible. + * + * No specific cleanup is required by the caller - everything is taken care of automatically + * as soon as you exit the [block] block. + * + * Usage example: + * ```kotlin + * // create a short-lived environment and resolve all the needed samples + * val sample = sampleAnalysisEnvironmentCreator.use { + * resolveSample(sampleSourceSet, "org.jetbrains.dokka.sample.functionName") + * } + * // process the samples + * // ... + * ``` + */ + public fun <T> use(block: SampleAnalysisEnvironment.() -> T): T +} diff --git a/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet.kt b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet.kt new file mode 100644 index 00000000..41b3fa5c --- /dev/null +++ b/dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet.kt @@ -0,0 +1,45 @@ +/* + * 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.sample + +/** + * Represents a sample code snippet of a Kotlin function. The snippet includes both file + * import directives (all, even unused) and the sample function body. + * + * @property imports list of import statement values, without the `import` prefix. + * Contains no blank lines. Example of a single value: `com.example.pckg.MyClass.function`. + * @property body body of the sample function, without the function name or curly braces, only the inner body. + * Common minimal indent of all lines is trimmed. Leading and trailing line breaks are removed. + * Trailing whitespaces are removed. Example: given the sample function `fun foo() { println("foo") }`, + * the sample body will be `println("foo")`. + * + * @see SampleAnalysisEnvironment for how to acquire it + */ +public class SampleSnippet( + public val imports: List<String>, + public val body: String +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as SampleSnippet + + if (imports != other.imports) return false + if (body != other.body) return false + + return true + } + + override fun hashCode(): Int { + var result = imports.hashCode() + result = 31 * result + body.hashCode() + return result + } + + override fun toString(): String { + return "SampleSnippet(imports=$imports, body='$body')" + } +} 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 index 618e28a8..3b8a2afd 100644 --- 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 @@ -4,52 +4,515 @@ package org.jetbrains.dokka.analysis.test.sample +import org.jetbrains.dokka.analysis.kotlin.sample.SampleSnippet import org.jetbrains.dokka.analysis.test.api.kotlinJvmTestProject +import org.jetbrains.dokka.analysis.test.api.mixedJvmTestProject import org.jetbrains.dokka.analysis.test.api.useServices -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotNull +import org.jetbrains.dokka.analysis.test.api.util.CollectingDokkaConsoleLogger +import org.jetbrains.dokka.analysis.test.api.util.singleSourceSet +import org.junit.jupiter.api.Tag +import kotlin.test.* class SampleAnalysisTest { @Test - fun `should return sources of a kotlin sample`() { + fun `should resolve a valid sample if set via the samples option`() { val testProject = kotlinJvmTestProject { dokkaConfiguration { kotlinSourceSet { - additionalSourceRoots = setOf("/samples") + samples = setOf("/samples/collections.kt") } } - sampleFile("/samples/stringListOf-sample.kt", fqPackageName = "org.jetbrains.dokka.sample.generator") { + sampleFile("/samples/collections.kt", fqPackageName = "org.jetbrains.dokka.sample.collections") { +""" 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() + fun specificPositionOperations() { + val numbers = mutableListOf(1, 2, 3, 4) + numbers.add(5) + numbers.removeAt(1) + numbers[0] = 0 + numbers.shuffle() + if (numbers.size > 0) { + println(numbers) + } } """ } } testProject.useServices { context -> - val sampleSourceSet = context.configuration.sourceSets.single() + val sample = sampleAnalysisEnvironmentCreator.use { + resolveSample( + sourceSet = context.singleSourceSet(), + fullyQualifiedLink = "org.jetbrains.dokka.sample.collections.specificPositionOperations" + ) + } + assertNotNull(sample) + + val expectedImports = listOf( + "org.jetbrains.dokka.DokkaConfiguration", + "org.jetbrains.dokka.DokkaGenerator", + "org.jetbrains.dokka.utilities.DokkaLogger" + ) - val sampleProvider = sampleProviderFactory.build() - val sample = sampleProvider.getSample(sampleSourceSet, "org.jetbrains.dokka.sample.generator.runGenerator") + val expectedBody = """ + val numbers = mutableListOf(1, 2, 3, 4) + numbers.add(5) + numbers.removeAt(1) + numbers[0] = 0 + numbers.shuffle() + if (numbers.size > 0) { + println(numbers) + } + """.trimIndent() + + assertEquals(expectedImports, sample.imports) + assertEquals(expectedBody, sample.body) + } + } + + @Test + @Tag("onlyDescriptors") // TODO #3359 + fun `should resolve a valid sample if set via the additionalSourceRoots option`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + additionalSourceRoots = setOf("/samples") + } + } + sampleFile("/samples/collections.kt", fqPackageName = "org.jetbrains.dokka.sample.collections") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + import org.jetbrains.dokka.DokkaGenerator + import org.jetbrains.dokka.utilities.DokkaLogger + + fun specificPositionOperations() { + val numbers = mutableListOf(1, 2, 3, 4) + numbers.add(5) + numbers.removeAt(1) + numbers[0] = 0 + numbers.shuffle() + if (numbers.size > 0) { + println(numbers) + } + } + """ + } + } + + testProject.useServices { context -> + val sample = sampleAnalysisEnvironmentCreator.use { + resolveSample( + sourceSet = context.singleSourceSet(), + fullyQualifiedLink = "org.jetbrains.dokka.sample.collections.specificPositionOperations" + ) + } assertNotNull(sample) val expectedImports = listOf( - "import org.jetbrains.dokka.DokkaConfiguration", - "import org.jetbrains.dokka.DokkaGenerator", - "import org.jetbrains.dokka.utilities.DokkaLogger" - ).joinToString(separator = "\n") + "org.jetbrains.dokka.DokkaConfiguration", + "org.jetbrains.dokka.DokkaGenerator", + "org.jetbrains.dokka.utilities.DokkaLogger" + ) + + val expectedBody = """ + val numbers = mutableListOf(1, 2, 3, 4) + numbers.add(5) + numbers.removeAt(1) + numbers[0] = 0 + numbers.shuffle() + if (numbers.size > 0) { + println(numbers) + } + """.trimIndent() + + assertEquals(expectedImports, sample.imports) + assertEquals(expectedBody, sample.body) + } + } + + @Test + @Tag("onlyDescriptors") // TODO #3359 + fun `should resolve a valid sample function that exists in the main source set`() { + val testProject = kotlinJvmTestProject { + ktFile("org/jetbrains/dokka/test/MyKotlinFile.kt") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + + fun myAverageTopLevelFunction() { + println("hello from the average top level function") + } + """ + } + } + + testProject.useServices { context -> + val sample = sampleAnalysisEnvironmentCreator.use { + resolveSample(context.singleSourceSet(), "org.jetbrains.dokka.test.myAverageTopLevelFunction") + } + assertNotNull(sample) + + val expectedImports = listOf("org.jetbrains.dokka.DokkaConfiguration") + val expectedBody = "println(\"hello from the average top level function\")" + + assertEquals(expectedImports, sample.imports) + assertEquals(expectedBody, sample.body) + } + } + + @Test + fun `should resolve a valid sample in the root package`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + samples = setOf("/samples/TopLevelSample.kt") + } + } + + sampleFile("/samples/TopLevelSample.kt", fqPackageName = "") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + + fun foo() { + println("hello from the root") + } + """ + } + } + + testProject.useServices { context -> + val sample = sampleAnalysisEnvironmentCreator.use { + resolveSample(context.singleSourceSet(), "foo") + } + assertNotNull(sample) + + val expectedImports = listOf("org.jetbrains.dokka.DokkaConfiguration") + val expectedBody = "println(\"hello from the root\")" + + assertEquals(expectedImports, sample.imports) + assertEquals(expectedBody, sample.body) + } + } + + @Test + fun `should resolve a valid sample function from a class in the root package`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + samples = setOf("/samples/RootClassSample.kt") + } + } + + sampleFile("/samples/RootClassSample.kt", fqPackageName = "") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + + class RootClass { + fun foo() { + println("hello from within a root class") + } + } + """ + } + } + + testProject.useServices { context -> + val sample = sampleAnalysisEnvironmentCreator.use { + resolveSample(context.singleSourceSet(), "RootClass.foo") + } + assertNotNull(sample) + + val expectedImports = listOf("org.jetbrains.dokka.DokkaConfiguration") + val expectedBody = "println(\"hello from within a root class\")" + + assertEquals(expectedImports, sample.imports) + assertEquals(expectedBody, sample.body) + } + } + + @Test + fun `should resolve a valid sample function from a class`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + samples = setOf("/samples/SampleWithinClass.kt") + } + } + + sampleFile("/samples/SampleWithinClass.kt", fqPackageName = "samples") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + + package samples + + class SampleWithinClass { + fun foo() { + println("hello from within a class") + } + } + """ + } + } + + testProject.useServices { context -> + val sample = sampleAnalysisEnvironmentCreator.use { + resolveSample(context.singleSourceSet(), "samples.SampleWithinClass.foo") + } + assertNotNull(sample) + + val expectedImports = listOf("org.jetbrains.dokka.DokkaConfiguration") + val expectedBody = "println(\"hello from within a class\")" + + assertEquals(expectedImports, sample.imports) + assertEquals(expectedBody, sample.body) + } + } + + @Test + fun `should return null for non-existing sample`() { + val testProject = kotlinJvmTestProject { + // nothing + } + + testProject.useServices { context -> + val nonExistingSample = sampleAnalysisEnvironmentCreator.use { + resolveSample(context.singleSourceSet(), "com.example.non.existing.sampleFunction") + } + + assertNull(nonExistingSample) + } + } + + @Test + fun `should return null if sample is resolved just by class name`() { + val testProject = kotlinJvmTestProject { + dokkaConfiguration { + kotlinSourceSet { + samples = setOf("/samples/FooSampleFile.kt") + } + } + sampleFile("/samples/FooSampleFile.kt", fqPackageName = "org.jetbrains.dokka.sample") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + + fun topLevelFunction() {} + + class FooSampleClass { + fun foo() { + println("foo") + } + } + """ + } + } + + val collectingLogger = CollectingDokkaConsoleLogger() + testProject.useServices(collectingLogger) { context -> + val sampleByClassName = sampleAnalysisEnvironmentCreator.use { + resolveSample(context.singleSourceSet(), "org.jetbrains.dokka.sample.FooSampleClass") + } + assertNull(sampleByClassName) + } + + val containsNonKotlinSampleLinkLog = collectingLogger.collectedLogMessages.contains( + "Unable to process a @sample link: \"org.jetbrains.dokka.sample.FooSampleClass\". " + + "Only function links allowed." + ) + assertTrue(containsNonKotlinSampleLinkLog) + } + + @Test + @Tag("onlyDescriptors") // TODO #3359 + fun `should return null if trying to resolve a non-kotlin sample link`() { + val testProject = mixedJvmTestProject { + kotlinSourceDirectory { + javaFile("org/jetbrains/test/sample/JavaClass.java") { + +""" + public class JavaClass { + public void foo() { + System.out.println("foo"); + } + } + """ + } + ktFile("org/jetbrains/test/sample/KotlinFile.kt") { + +""" + fun foo() {} + """ + } + } + } + + val collectingLogger = CollectingDokkaConsoleLogger() + testProject.useServices(collectingLogger) { context -> + sampleAnalysisEnvironmentCreator.use { + val kotlinSourceSet = context.singleSourceSet() + + val byClassName = resolveSample(kotlinSourceSet, "org.jetbrains.test.sample.JavaClass") + assertNull(byClassName) + + val byClassFunctionName = resolveSample(kotlinSourceSet, "org.jetbrains.test.sample.JavaClass.foo") + assertNull(byClassFunctionName) + } + } + + val containsNonKotlinSampleLinkLog = collectingLogger.collectedLogMessages.contains( + "Unable to resolve non-Kotlin @sample links: \"org.jetbrains.test.sample.JavaClass\"" + ) + assertTrue(containsNonKotlinSampleLinkLog) + } + + @Test + @Tag("onlyDescriptors") // TODO #3359 + fun `should filter out empty import statement lines`() { + val testProject = kotlinJvmTestProject { + ktFile("org/jetbrains/dokka/test/MyKotlinFile.kt") { + +""" + import org.jetbrains.dokka.DokkaConfiguration + + import org.jetbrains.dokka.DokkaGenerator + + import org.jetbrains.dokka.utilities.DokkaLogger + + fun sample() { + println("hello from sample") + } + """ + } |
