aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/analysis-kotlin-api/src
diff options
context:
space:
mode:
authorIgnat Beresnev <ignat.beresnev@jetbrains.com>2023-11-21 11:28:45 +0100
committerGitHub <noreply@github.com>2023-11-21 11:28:45 +0100
commit6fbc2221ff309995c605161b51d4d64cbabddd51 (patch)
treea6476838442e47b3bbc03d753c74c84f58f67b1e /dokka-subprojects/analysis-kotlin-api/src
parent9ce37affaa2c1199807c08e13485740ea993994e (diff)
downloaddokka-6fbc2221ff309995c605161b51d4d64cbabddd51.tar.gz
dokka-6fbc2221ff309995c605161b51d4d64cbabddd51.tar.bz2
dokka-6fbc2221ff309995c605161b51d4d64cbabddd51.zip
Stabilize Sample analysis API (#3195)
Diffstat (limited to 'dokka-subprojects/analysis-kotlin-api/src')
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/KotlinAnalysisPlugin.kt10
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/InternalKotlinAnalysisPlugin.kt2
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/internal/SampleProvider.kt36
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironment.kt43
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleAnalysisEnvironmentCreator.kt38
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/main/kotlin/org/jetbrains/dokka/analysis/kotlin/sample/SampleSnippet.kt45
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/test/kotlin/org/jetbrains/dokka/analysis/test/sample/SampleAnalysisTest.kt495
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/TestProject.kt18
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestAnalysisServices.kt4
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/analysis/TestProjectAnalyzer.kt83
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaConfigurationBuilder.kt2
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestProject.kt6
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmConfigurationBuilder.kt2
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmTestProject.kt6
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestProject.kt18
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/DokkaLoggerUtils.kt63
-rw-r--r--dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/TestAnalysisApiUtils.kt27
17 files changed, 793 insertions, 105 deletions
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")
+ }
+ """
+ }
+ }
+
+ testProject.useServices { context ->
+ val sample = sampleAnalysisEnvironmentCreator.use {
+ resolveSample(context.singleSourceSet(), "org.jetbrains.dokka.test.sample")
+ }
+ assertNotNull(sample)
- val expectedBody = "DokkaGenerator(configuration, logger).generate()"
+ val expectedImports = listOf(
+ "org.jetbrains.dokka.DokkaConfiguration",
+ "org.jetbrains.dokka.DokkaGenerator",
+ "org.jetbrains.dokka.utilities.DokkaLogger",
+ )
+ val expectedBody = "println(\"hello from sample\")"
assertEquals(expectedImports, sample.imports)
assertEquals(expectedBody, sample.body)
}
}
+
+ @Test
+ @Tag("onlyDescriptors") // TODO #3359
+ fun `should return an empty list of imports if sample file has none`() {
+ val testProject = kotlinJvmTestProject {
+ ktFile("org/jetbrains/dokka/test/MyKotlinFile.kt") {
+ +"""
+ fun sample() {
+ println("hello from sample")
+ }
+ """
+ }
+ }
+
+ testProject.useServices { context ->
+ val sample = sampleAnalysisEnvironmentCreator.use {
+ resolveSample(context.singleSourceSet(), "org.jetbrains.dokka.test.sample")
+ }
+ assertNotNull(sample)
+
+ assertTrue(sample.imports.isEmpty())
+
+ val expectedBody = "println(\"hello from sample\")"
+ assertEquals(expectedBody, sample.body)
+
+ }
+ }
+
+ @Test
+ @Tag("onlyDescriptors") // TODO #3359
+ fun `should filter out leading and trailing line breaks`() {
+ val testProject = kotlinJvmTestProject {
+ ktFile("org/jetbrains/dokka/test/MyKotlinFile.kt") {
+ +"""
+ fun sample() {
+
+
+ println("hello from sample")
+
+
+
+ }
+ """
+ }
+ }
+
+ testProject.useServices { context ->
+ val sample = sampleAnalysisEnvironmentCreator.use {
+ resolveSample(context.singleSourceSet(), "org.jetbrains.dokka.test.sample")
+ }
+ assertNotNull(sample)
+
+ val expectedBody = "println(\"hello from sample\")"
+ assertEquals(expectedBody, sample.body)
+ }
+ }
+
+ @Test
+ @Tag("onlyDescriptors") // TODO #3359
+ fun `should filter out trailing whitespace`() {
+ val testProject = kotlinJvmTestProject {
+ ktFile("org/jetbrains/dokka/test/MyKotlinFile.kt") {
+ +"""
+ fun sample() {
+ println("hello from sample")
+ }
+ """
+ }
+ }
+
+ testProject.useServices { context ->
+ val sample = sampleAnalysisEnvironmentCreator.use {
+ resolveSample(context.singleSourceSet(), "org.jetbrains.dokka.test.sample")
+ }
+ assertNotNull(sample)
+
+ val expectedBody = "println(\"hello from sample\")"
+ assertEquals(expectedBody, sample.body)
+ }
+ }
+
+ @Test
+ fun `should see two identical snippets as equal`() {
+ val firstSnippet = createHardcodedSnippet()
+ val secondSnippet = createHardcodedSnippet()
+
+ assertEquals(firstSnippet, secondSnippet)
+ }
+
+ @Test
+ fun `should return same hashcode for two equal sample snippets`() {
+ val firstSnippet = createHardcodedSnippet()
+ val secondSnippet = createHardcodedSnippet()
+
+ assertEquals(firstSnippet.hashCode(), secondSnippet.hashCode())
+ }
+
+ private fun createHardcodedSnippet(): SampleSnippet {
+ return SampleSnippet(
+ imports = listOf(
+ "org.jetbrains.dokka.DokkaConfiguration",
+ "org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet",
+ ),
+ body = """
+ class Foo {
+ fun bar(): String = TODO()
+ }
+ """.trimIndent()
+ )
+ }
+
+ @Test
+ @Ignore // TODO [beresnev] should be implemented when there's api for KMP projects
+ fun `should return null for existing sample when resolving with the wrong source set`() {}
}
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
index 9c0fa936..e000bb69 100644
--- 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
@@ -8,10 +8,13 @@ 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.analysis.defaultAnalysisLogger
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.CollectingDokkaConsoleLogger
import org.jetbrains.dokka.analysis.test.api.util.withTempDirectory
import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.utilities.DokkaLogger
/**
* Represents a virtual test project (as if it's user-defined) that will be used to run Dokka.
@@ -66,8 +69,11 @@ interface TestProject {
*
* val module: DModule = testProject.parse()
* ```
+ *
+ * @param logger logger to be used for running Dokka and tests. Custom loggers like [CollectingDokkaConsoleLogger]
+ * can be useful in verifying the behavior.
*/
-fun TestProject.parse(): DModule = TestProjectAnalyzer.parse(this)
+fun TestProject.parse(logger: DokkaLogger = defaultAnalysisLogger): DModule = TestProjectAnalyzer.parse(this, logger)
/**
* Runs Dokka on the given [TestProject] and provides not only the resulting documentable model,
@@ -88,10 +94,16 @@ fun TestProject.parse(): DModule = TestProjectAnalyzer.parse(this)
* val allPackageDocs: SourceSetDependent<DocumentationNode> = moduleAndPackageDocumentationReader.read(pckg)
* }
* ```
+ *
+ * @param logger logger to be used for running Dokka and tests. Custom loggers like [CollectingDokkaConsoleLogger]
+ * can be useful in verifying the behavior.
*/
-fun TestProject.useServices(block: TestAnalysisServices.(context: TestAnalysisContext) -> Unit) {
+fun TestProject.useServices(
+ logger: DokkaLogger = defaultAnalysisLogger,
+ block: TestAnalysisServices.(context: TestAnalysisContext) -> Unit
+) {
withTempDirectory { tempDirectory ->
- val (services, context) = TestProjectAnalyzer.analyze(this, tempDirectory)
+ val (services, context) = TestProjectAnalyzer.analyze(this, tempDirectory, logger)
services.block(context)
}
}
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
index ab70bbd4..f729838d 100644
--- 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
@@ -6,7 +6,7 @@ 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
+import org.jetbrains.dokka.analysis.kotlin.sample.SampleAnalysisEnvironmentCreator
/**
* Services exposed in [KotlinAnalysisPlugin] that are ready to be used.
@@ -15,6 +15,6 @@ import org.jetbrains.dokka.analysis.kotlin.internal.SampleProviderFactory
* It is analogous to calling `context.plugin<KotlinAnalysisPlugin>().querySingle { serviceName }`.
*/
class TestAnalysisServices(
- val sampleProviderFactory: SampleProviderFactory,
+ val sampleAnalysisEnvironmentCreator: SampleAnalysisEnvironmentCreator,
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
index 1668b53f..674c6d47 100644
--- 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
@@ -6,6 +6,7 @@ package org.jetbrains.dokka.analysis.test.api.analysis
import org.jetbrains.dokka.CoreExtensions
import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.analysis.kotlin.KotlinAnalysisPlugin
import org.jetbrains.dokka.analysis.kotlin.internal.InternalKotlinAnalysisPlugin
import org.jetbrains.dokka.analysis.test.api.TestDataFile
import org.jetbrains.dokka.analysis.test.api.TestProject
@@ -21,16 +22,14 @@ 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.DokkaLogger
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.
+ * The default logger used for running Dokka and analyzing projects.
*/
-val analysisLogger = DokkaConsoleLogger(minLevel = LoggingLevel.INFO)
+val defaultAnalysisLogger = DokkaConsoleLogger(minLevel = LoggingLevel.DEBUG)
/**
* Analyzer of the test projects, it is essentially a very simple Dokka runner.
@@ -47,7 +46,7 @@ val analysisLogger = DokkaConsoleLogger(minLevel = LoggingLevel.INFO)
* 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 {
+internal object TestProjectAnalyzer {
/**
* A quick way to analyze a [TestProject], for cases when only the documentable
@@ -58,11 +57,11 @@ object TestProjectAnalyzer {
*
* @see [TestProject.parse] for a user-friendly way to call it
*/
- fun parse(testProject: TestProject): DModule {
+ fun parse(testProject: TestProject, logger: DokkaLogger): 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)
+ return withTempDirectory(logger) { tempDirectory ->
+ val (_, context) = testProject.initialize(outputDirectory = tempDirectory, logger)
+ generateDocumentableModel(context, logger)
}
}
@@ -80,14 +79,15 @@ object TestProjectAnalyzer {
*/
fun analyze(
testProject: TestProject,
- persistentDirectory: File
+ persistentDirectory: File,
+ logger: DokkaLogger
): Pair<TestAnalysisServices, TestAnalysisContext> {
- val (dokkaConfiguration, dokkaContext) = testProject.initialize(outputDirectory = persistentDirectory)
- val analysisServices = createTestAnalysisServices(dokkaContext)
+ val (dokkaConfiguration, dokkaContext) = testProject.initialize(outputDirectory = persistentDirectory, logger)
+ val analysisServices = createTestAnalysisServices(dokkaContext, logger)
val testAnalysisContext = TestAnalysisContext(
context = dokkaContext,
configuration = dokkaConfiguration,
- module = generateDocumentableModel(dokkaContext)
+ module = generateDocumentableModel(dokkaContext, logger)
)
return analysisServices to testAnalysisContext
}
@@ -96,28 +96,31 @@ object TestProjectAnalyzer {
* Prepares this [TestProject] for analysis by creating
* the test files, setting up context and configuration.
*/
- private fun TestProject.initialize(outputDirectory: File): Pair<DokkaConfiguration, DokkaContext> {
- analysisLogger.progress("Initializing and verifying project $this")
+ private fun TestProject.initialize(
+ outputDirectory: File,
+ logger: DokkaLogger
+ ): Pair<DokkaConfiguration, DokkaContext> {
+ logger.progress("Initializing and verifying project $this")
this.verify()
require(outputDirectory.isDirectory) {
"outputDirectory has to exist and be a directory: $outputDirectory"
}
- this.initializeTestFiles(relativeToDir = outputDirectory)
+ this.initializeTestFiles(relativeToDir = outputDirectory, logger)
- analysisLogger.progress("Creating configuration and context")
+ logger.progress("Creating configuration and context")
val testDokkaConfiguration = this.getConfiguration()
val dokkaConfiguration = testDokkaConfiguration.toDokkaConfiguration(projectDir = outputDirectory).also {
it.verify()
}
- return dokkaConfiguration to createContext(dokkaConfiguration)
+ return dokkaConfiguration to createContext(dokkaConfiguration, logger)
}
/**
* 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")
+ private fun TestProject.initializeTestFiles(relativeToDir: File, logger: DokkaLogger) {
+ logger.progress("Initializing test files relative to the \"$relativeToDir\" directory")
this.getTestData().getFiles().forEach {
val testDataFile = relativeToDir.resolve(it.pathFromProjectRoot.removePrefix("/"))
@@ -128,7 +131,7 @@ object TestProjectAnalyzer {
throw IllegalStateException("Unable to create dirs \"${testDataFile.parentFile}\"", e)
}
- analysisLogger.debug("Creating \"${testDataFile.absolutePath}\"")
+ logger.debug("Creating \"${testDataFile.absolutePath}\"")
check(testDataFile.createNewFile()) {
"Unable to create a test file: ${testDataFile.absolutePath}"
}
@@ -163,11 +166,11 @@ object TestProjectAnalyzer {
}
}
- private fun createContext(dokkaConfiguration: DokkaConfiguration): DokkaContext {
- analysisLogger.progress("Creating DokkaContext from test configuration")
+ private fun createContext(dokkaConfiguration: DokkaConfiguration, logger: DokkaLogger): DokkaContext {
+ logger.progress("Creating DokkaContext from test configuration")
return DokkaContext.create(
configuration = dokkaConfiguration,
- logger = analysisLogger,
+ logger = logger,
pluginOverrides = listOf()
)
}
@@ -176,12 +179,12 @@ object TestProjectAnalyzer {
* 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")
+ private fun generateDocumentableModel(context: DokkaContext, logger: DokkaLogger): DModule {
+ logger.progress("Generating the documentable model")
val sourceSetModules = context
.configuration
.sourceSets
- .map { sourceSet -> translateSources(sourceSet, context) }
+ .map { sourceSet -> translateSources(sourceSet, context, logger) }
.flatten()
if (sourceSetModules.isEmpty()) {
@@ -196,12 +199,16 @@ object TestProjectAnalyzer {
* Translates input source files to the documentable model by using
* all registered [SourceToDocumentableTranslator] core extensions.
*/
- private fun translateSources(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext): List<DModule> {
+ private fun translateSources(
+ sourceSet: DokkaConfiguration.DokkaSourceSet,
+ context: DokkaContext,
+ logger: DokkaLogger
+ ): List<DModule> {
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}")
+ logger.debug("Translating sources for ${sourceSet.sourceSetID}")
return translators.map { it.invoke(sourceSet, context) }
}
@@ -212,12 +219,18 @@ object TestProjectAnalyzer {
* 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<InternalKotlinAnalysisPlugin>()
+ private fun createTestAnalysisServices(
+ context: DokkaContext,
+ logger: DokkaLogger
+ ): TestAnalysisServices {
+ logger.progress("Creating analysis services")
+ val publicAnalysisPlugin = context.plugin<KotlinAnalysisPlugin>()
+ val internalAnalysisPlugin = context.plugin<InternalKotlinAnalysisPlugin>()
return TestAnalysisServices(
- sampleProviderFactory = internalPlugin.querySingle { sampleProviderFactory },
- moduleAndPackageDocumentationReader = internalPlugin.querySingle { moduleAndPackageDocumentationReader }
+ sampleAnalysisEnvironmentCreator = publicAnalysisPlugin.querySingle { sampleAnalysisEnvironmentCreator },
+ moduleAndPackageDocumentationReader = internalAnalysisPlugin.querySingle {
+ moduleAndPackageDocumentationReader
+ }
)
}
}
diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaConfigurationBuilder.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaConfigurationBuilder.kt
index 156fc4e4..baabeed7 100644
--- a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaConfigurationBuilder.kt
+++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaConfigurationBuilder.kt
@@ -49,7 +49,7 @@ class JavaTestSourceSetBuilder : BaseTestDokkaSourceSetBuilder() {
return TestDokkaSourceSet(
analysisPlatform = Platform.jvm,
displayName = "JavaJvmSourceSet",
- sourceSetID = DokkaSourceSetID(scopeId = "project", sourceSetName = "java"),
+ sourceSetID = JavaTestProject.DEFAULT_SOURCE_SET_ID,
dependentSourceSets = setOf(),
sourceRoots = additionalSourceRoots + setOf(JavaTestProject.DEFAULT_SOURCE_ROOT),
classpath = additionalClasspath, // TODO [beresnev] is kotlin jvm stdlib needed here?
diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestProject.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestProject.kt
index 39f0f0f6..9ce85961 100644
--- a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestProject.kt
+++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/java/JavaTestProject.kt
@@ -4,6 +4,7 @@
package org.jetbrains.dokka.analysis.test.api.jvm.java
+import org.jetbrains.dokka.DokkaSourceSetID
import org.jetbrains.dokka.analysis.test.api.TestData
import org.jetbrains.dokka.analysis.test.api.TestDataFile
import org.jetbrains.dokka.analysis.test.api.TestProject
@@ -67,7 +68,8 @@ class JavaTestProject : TestProject, JavaFileCreator, MdFileCreator {
")"
}
- internal companion object {
- internal const val DEFAULT_SOURCE_ROOT = "/src/main/java"
+ companion object {
+ const val DEFAULT_SOURCE_ROOT = "/src/main/java"
+ val DEFAULT_SOURCE_SET_ID = DokkaSourceSetID(scopeId = "project", sourceSetName = "java")
}
}
diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmConfigurationBuilder.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmConfigurationBuilder.kt
index e5424ead..79028056 100644
--- a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmConfigurationBuilder.kt
+++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmConfigurationBuilder.kt
@@ -44,7 +44,7 @@ class KotlinJvmTestSourceSetBuilder : BaseTestDokkaSourceSetBuilder() {
return TestDokkaSourceSet(
analysisPlatform = Platform.jvm,
displayName = "KotlinJvmSourceSet",
- sourceSetID = DokkaSourceSetID(scopeId = "project", sourceSetName = "kotlin"),
+ sourceSetID = KotlinJvmTestProject.DEFAULT_SOURCE_SET_ID,
dependentSourceSets = setOf(),
sourceRoots = additionalSourceRoots + setOf(KotlinJvmTestProject.DEFAULT_SOURCE_ROOT),
classpath = additionalClasspath + setOf(getKotlinJvmStdlibJarPath()),
diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmTestProject.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmTestProject.kt
index 178a1dc3..d67e1321 100644
--- a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmTestProject.kt
+++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/kotlin/KotlinJvmTestProject.kt
@@ -4,6 +4,7 @@
package org.jetbrains.dokka.analysis.test.api.jvm.kotlin
+import org.jetbrains.dokka.DokkaSourceSetID
import org.jetbrains.dokka.analysis.test.api.TestData
import org.jetbrains.dokka.analysis.test.api.TestDataFile
import org.jetbrains.dokka.analysis.test.api.TestProject
@@ -85,8 +86,9 @@ class KotlinJvmTestProject : TestProject, KtFileCreator, MdFileCreator, KotlinSa
")"
}
- internal companion object {
- internal const val DEFAULT_SOURCE_ROOT = "/src/main/kotlin"
+ companion object {
+ const val DEFAULT_SOURCE_ROOT = "/src/main/kotlin"
+ val DEFAULT_SOURCE_SET_ID = DokkaSourceSetID(scopeId = "project", sourceSetName = "kotlin")
}
}
diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestProject.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestProject.kt
index 32b7ce0a..45ca53e2 100644
--- a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestProject.kt
+++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/jvm/mixed/MixedJvmTestProject.kt
@@ -10,6 +10,9 @@ import org.jetbrains.dokka.analysis.test.api.TestProject
import org.jetbrains.dokka.analysis.test.api.configuration.TestDokkaConfiguration
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.kotlin.sample.KotlinSampleFileCreator
+import org.jetbrains.dokka.analysis.test.api.kotlin.sample.KotlinSampleTestData
+import org.jetbrains.dokka.analysis.test.api.kotlin.sample.KotlinSampleTestDataFile
import org.jetbrains.dokka.analysis.test.api.markdown.MarkdownTestData
import org.jetbrains.dokka.analysis.test.api.markdown.MarkdownTestDataFile
import org.jetbrains.dokka.analysis.test.api.markdown.MdFileCreator
@@ -20,13 +23,14 @@ import org.jetbrains.dokka.analysis.test.api.util.flatListOf
/**
* @see mixedJvmTestProject for an explanation and a convenient way to construct this project
*/
-class MixedJvmTestProject : TestProject, MdFileCreator {
+class MixedJvmTestProject : TestProject, MdFileCreator, KotlinSampleFileCreator {
private val projectConfigurationBuilder = MixedJvmTestConfigurationBuilder()
private val kotlinSourceDirectory = MixedJvmTestData(pathToSources = KotlinJvmTestProject.DEFAULT_SOURCE_ROOT)
private val javaSourceDirectory = MixedJvmTestData(pathToSources = JavaTestProject.DEFAULT_SOURCE_ROOT)
private val markdownTestData = MarkdownTestData()
+ private val kotlinSampleTestData = KotlinSampleTestData()
@AnalysisTestDslMarker
fun dokkaConfiguration(fillConfiguration: MixedJvmTestConfigurationBuilder.() -> Unit) {
@@ -48,6 +52,15 @@ class MixedJvmTestProject : TestProject, MdFileCreator {
markdownTestData.mdFile(pathFromProjectRoot, fillFile)
}
+ @AnalysisTestDslMarker
+ override fun sampleFile(
+ pathFromProjectRoot: String,
+ fqPackageName: String,
+ fillFile: KotlinSampleTestDataFile.() -> Unit
+ ) {
+ kotlinSampleTestData.sampleFile(pathFromProjectRoot, fqPackageName, fillFile)
+ }
+
override fun verify() {
projectConfigurationBuilder.verify()
}
@@ -62,7 +75,8 @@ class MixedJvmTestProject : TestProject, MdFileCreator {
return flatListOf(
this@MixedJvmTestProject.kotlinSourceDirectory.getFiles(),
this@MixedJvmTestProject.javaSourceDirectory.getFiles(),
- this@MixedJvmTestProject.markdownTestData.getFiles()
+ this@MixedJvmTestProject.markdownTestData.getFiles(),
+ this@MixedJvmTestProject.kotlinSampleTestData.getFiles()
)
}
}
diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/DokkaLoggerUtils.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/DokkaLoggerUtils.kt
new file mode 100644
index 00000000..87de4540
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/DokkaLoggerUtils.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.util
+
+import org.jetbrains.dokka.utilities.DokkaConsoleLogger
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.jetbrains.dokka.utilities.LoggingLevel
+
+/**
+ * Prints messages to the console according to the passed `consoleMinLevel` parameter,
+ * and collects **ALL** log messages, regarding of the set logging level.
+ *
+ * Useful if you need to verify that user-friendly log messages were emitted,
+ * in case they outline actionable problems or help solve a problem and are
+ * considered to be a vital part of this product.
+ *
+ * The collected messages can be retrieved by invoking [collectedLogMessages].
+ */
+class CollectingDokkaConsoleLogger(
+ consoleMinLoggingLevel: LoggingLevel = LoggingLevel.INFO
+) : DokkaLogger {
+
+ private val consoleLogger = DokkaConsoleLogger(consoleMinLoggingLevel)
+ private val _collectedLogMessages = mutableListOf<String>()
+
+ val collectedLogMessages: List<String> = _collectedLogMessages
+
+ override var warningsCount: Int
+ get() = consoleLogger.warningsCount
+ set(value) { consoleLogger.warningsCount = value }
+
+ override var errorsCount: Int
+ get() = consoleLogger.errorsCount
+ set(value) { consoleLogger.errorsCount = value }
+
+
+ override fun debug(message: String) {
+ _collectedLogMessages.add(message)
+ consoleLogger.debug(message)
+ }
+
+ override fun info(message: String) {
+ _collectedLogMessages.add(message)
+ consoleLogger.info(message)
+ }
+
+ override fun progress(message: String) {
+ _collectedLogMessages.add(message)
+ consoleLogger.progress(message)
+ }
+
+ override fun warn(message: String) {
+ _collectedLogMessages.add(message)
+ consoleLogger.warn(message)
+ }
+
+ override fun error(message: String) {
+ _collectedLogMessages.add(message)
+ consoleLogger.error(message)
+ }
+}
diff --git a/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/TestAnalysisApiUtils.kt b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/TestAnalysisApiUtils.kt
new file mode 100644
index 00000000..18a04ae5
--- /dev/null
+++ b/dokka-subprojects/analysis-kotlin-api/src/testFixtures/kotlin/org/jetbrains/dokka/analysis/test/api/util/TestAnalysisApiUtils.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.util
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.DokkaSourceSetID
+import org.jetbrains.dokka.analysis.test.api.analysis.TestAnalysisContext
+import org.jetbrains.dokka.analysis.test.api.jvm.java.JavaTestProject
+import org.jetbrains.dokka.analysis.test.api.jvm.kotlin.KotlinJvmTestProject
+
+/**
+ * @return the only existing source set or an exception
+ */
+fun TestAnalysisContext.singleSourceSet(): DokkaConfiguration.DokkaSourceSet {
+ return this.configuration.sourceSets.single()
+}
+
+fun TestAnalysisContext.defaultKotlinSourceSet() = findSourceSetById(KotlinJvmTestProject.DEFAULT_SOURCE_SET_ID)
+fun TestAnalysisContext.defaultJavaSourceSet() = findSourceSetById(JavaTestProject.DEFAULT_SOURCE_SET_ID)
+
+fun TestAnalysisContext.findSourceSetById(dokkaSourceSetID: DokkaSourceSetID): DokkaConfiguration.DokkaSourceSet {
+ return this.configuration.sourceSets.single {
+ it.sourceSetID == dokkaSourceSetID
+ }
+}