aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/src/test/kotlin
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/base/src/test/kotlin')
-rw-r--r--plugins/base/src/test/kotlin/basic/DRITest.kt317
-rw-r--r--plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt42
-rw-r--r--plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt118
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt221
-rw-r--r--plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt103
-rw-r--r--plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt611
-rw-r--r--plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt459
-rw-r--r--plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt456
-rw-r--r--plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt254
-rw-r--r--plugins/base/src/test/kotlin/enums/EnumsTest.kt234
-rw-r--r--plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt104
-rw-r--r--plugins/base/src/test/kotlin/expect/ExpectGenerator.kt13
-rw-r--r--plugins/base/src/test/kotlin/expect/ExpectTest.kt24
-rw-r--r--plugins/base/src/test/kotlin/expect/ExpectUtils.kt28
-rw-r--r--plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt173
-rw-r--r--plugins/base/src/test/kotlin/filter/EmptyPackagesFilterTest.kt63
-rw-r--r--plugins/base/src/test/kotlin/filter/VisibilityFilterTest.kt173
-rw-r--r--plugins/base/src/test/kotlin/issues/IssuesTest.kt73
-rw-r--r--plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt225
-rw-r--r--plugins/base/src/test/kotlin/locationProvider/DefaultLocationProviderTest.kt41
-rw-r--r--plugins/base/src/test/kotlin/markdown/KDocTest.kt47
-rw-r--r--plugins/base/src/test/kotlin/markdown/LinkTest.kt79
-rw-r--r--plugins/base/src/test/kotlin/markdown/ParserTest.kt1123
-rw-r--r--plugins/base/src/test/kotlin/model/ClassesTest.kt533
-rw-r--r--plugins/base/src/test/kotlin/model/CommentTest.kt332
-rw-r--r--plugins/base/src/test/kotlin/model/FunctionsTest.kt396
-rw-r--r--plugins/base/src/test/kotlin/model/InheritorsTest.kt95
-rw-r--r--plugins/base/src/test/kotlin/model/JavaTest.kt346
-rw-r--r--plugins/base/src/test/kotlin/model/PackagesTest.kt134
-rw-r--r--plugins/base/src/test/kotlin/model/PropertyTest.kt265
-rw-r--r--plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt54
-rw-r--r--plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt126
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt328
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt78
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt90
-rw-r--r--plugins/base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt139
-rw-r--r--plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt71
-rw-r--r--plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt175
-rw-r--r--plugins/base/src/test/kotlin/signatures/SignatureTest.kt379
-rw-r--r--plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt150
-rw-r--r--plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt403
-rw-r--r--plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt919
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt87
-rw-r--r--plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt144
-rw-r--r--plugins/base/src/test/kotlin/translators/utils.kt16
-rw-r--r--plugins/base/src/test/kotlin/utils/JsoupUtils.kt29
-rw-r--r--plugins/base/src/test/kotlin/utils/ModelUtils.kt37
-rw-r--r--plugins/base/src/test/kotlin/utils/TestUtils.kt79
-rw-r--r--plugins/base/src/test/kotlin/utils/contentUtils.kt243
49 files changed, 10629 insertions, 0 deletions
diff --git a/plugins/base/src/test/kotlin/basic/DRITest.kt b/plugins/base/src/test/kotlin/basic/DRITest.kt
new file mode 100644
index 00000000..559a2dbf
--- /dev/null
+++ b/plugins/base/src/test/kotlin/basic/DRITest.kt
@@ -0,0 +1,317 @@
+package basic
+
+import org.jetbrains.dokka.links.*
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.links.Nullable
+import org.jetbrains.dokka.links.TypeConstructor
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.pages.ClasslikePageNode
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.MemberPageNode
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+class DRITest : AbstractCoreTest() {
+ @Test
+ fun issue634() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package toplevel
+ |
+ |inline fun <T, R : Comparable<R>> Array<out T>.mySortBy(
+ | crossinline selector: (T) -> R?): Array<out T> = TODO()
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val expected = TypeConstructor(
+ "kotlin.Function1", listOf(
+ TypeParam(listOf(Nullable(TypeConstructor("kotlin.Any", emptyList())))),
+ Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType)))))
+ )
+ )
+ val actual = module.packages.single()
+ .functions.single()
+ .dri.callable?.params?.single()
+ assertEquals(expected, actual)
+ }
+ }
+ }
+
+ @Test
+ fun issue634WithImmediateNullableSelf() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package toplevel
+ |
+ |fun <T : Comparable<T>> Array<T>.doSomething(t: T?): Array<T> = TODO()
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType)))))
+ val actual = module.packages.single()
+ .functions.single()
+ .dri.callable?.params?.single()
+ assertEquals(expected, actual)
+ }
+ }
+ }
+
+ @Test
+ fun issue634WithGenericNullableReceiver() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package toplevel
+ |
+ |fun <T : Comparable<T>> T?.doSomethingWithNullable() = TODO()
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType)))))
+ val actual = module.packages.single()
+ .functions.single()
+ .dri.callable?.receiver
+ assertEquals(expected, actual)
+ }
+ }
+ }
+
+ @Test
+ fun issue642WithStarAndAny() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ analysisPlatform = "js"
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |
+ |open class Bar<Z>
+ |class ReBarBar : Bar<StringBuilder>()
+ |class Foo<out T : Comparable<*>, R : List<Bar<*>>>
+ |
+ |fun <T : Comparable<Any?>> Foo<T, *>.qux(): String = TODO()
+ |fun <T : Comparable<*>> Foo<T, *>.qux(): String = TODO()
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = { module ->
+ // DRI(//qux/Foo[TypeParam(bounds=[kotlin.Comparable[kotlin.Any?]]),*]#/PointingToFunctionOrClasslike/)
+ val expectedDRI = DRI(
+ "",
+ null,
+ Callable(
+ "qux", TypeConstructor(
+ "Foo", listOf(
+ TypeParam(
+ listOf(
+ TypeConstructor(
+ "kotlin.Comparable", listOf(
+ Nullable(TypeConstructor("kotlin.Any", emptyList()))
+ )
+ )
+ )
+ ),
+ StarProjection
+ )
+ ),
+ emptyList()
+ )
+ )
+
+ val driCount = module
+ .withDescendants()
+ .filterIsInstance<ContentPage>()
+ .sumBy { it.dri.count { dri -> dri == expectedDRI } }
+
+ assertEquals(1, driCount)
+ }
+ }
+ }
+
+ @Test
+ fun driForGenericClass(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package example
+ |
+ |class Sample<S>(first: S){ }
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = { module ->
+ val sampleClass = module.dfs { it.name == "Sample" } as ClasslikePageNode
+ val classDocumentable = sampleClass.documentable as DClass
+
+ assertEquals( "example/Sample///PointingToDeclaration/", sampleClass.dri.first().toString())
+ assertEquals("example/Sample///PointingToGenericParameters(0)/", classDocumentable.generics.first().dri.toString())
+ }
+ }
+ }
+
+ @Test
+ fun driForGenericFunction(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package example
+ |
+ |class Sample<S>(first: S){
+ | fun <T> genericFun(param1: String): Tuple<S,T> = TODO()
+ |}
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = { module ->
+ val sampleClass = module.dfs { it.name == "Sample" } as ClasslikePageNode
+ val functionNode = sampleClass.children.first { it.name == "genericFun" } as MemberPageNode
+ val functionDocumentable = functionNode.documentable as DFunction
+ val parameter = functionDocumentable.parameters.first()
+
+ assertEquals("example/Sample/genericFun/#kotlin.String/PointingToDeclaration/", functionNode.dri.first().toString())
+
+ assertEquals(1, functionDocumentable.parameters.size)
+ assertEquals("example/Sample/genericFun/#kotlin.String/PointingToCallableParameters(0)/", parameter.dri.toString())
+ //1 since from the function's perspective there is only 1 new generic declared
+ //The other one is 'inherited' from class
+ assertEquals( 1, functionDocumentable.generics.size)
+ assertEquals( "T", functionDocumentable.generics.first().name)
+ assertEquals( "example/Sample/genericFun/#kotlin.String/PointingToGenericParameters(0)/", functionDocumentable.generics.first().dri.toString())
+ }
+ }
+ }
+
+ @Test
+ fun driForFunctionNestedInsideInnerClass() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package example
+ |
+ |class Sample<S>(first: S){
+ | inner class SampleInner {
+ | fun foo(): S = TODO()
+ | }
+ |}
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = { module ->
+ val sampleClass = module.dfs { it.name == "Sample" } as ClasslikePageNode
+ val sampleInner = sampleClass.children.first { it.name == "SampleInner" } as ClasslikePageNode
+ val foo = sampleInner.children.first { it.name == "foo" } as MemberPageNode
+ val documentable = foo.documentable as DFunction
+
+ assertEquals(sampleClass.dri.first().toString(), (documentable.type as OtherParameter).declarationDRI.toString())
+ assertEquals(0, documentable.generics.size)
+ }
+ }
+ }
+
+ @Test
+ fun driForGenericExtensionFunction(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package example
+ |
+ | fun <T> List<T>.extensionFunction(): String = ""
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = { module ->
+ val extensionFunction = module.dfs { it.name == "extensionFunction" } as MemberPageNode
+ val documentable = extensionFunction.documentable as DFunction
+
+ assertEquals(
+ "example//extensionFunction/kotlin.collections.List[TypeParam(bounds=[kotlin.Any?])]#/PointingToDeclaration/",
+ extensionFunction.dri.first().toString()
+ )
+ assertEquals(1, documentable.generics.size)
+ assertEquals("T", documentable.generics.first().name)
+ assertEquals(
+ "example//extensionFunction/kotlin.collections.List[TypeParam(bounds=[kotlin.Any?])]#/PointingToGenericParameters(0)/",
+ documentable.generics.first().dri.toString()
+ )
+
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt b/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt
new file mode 100644
index 00000000..bceb79ae
--- /dev/null
+++ b/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt
@@ -0,0 +1,42 @@
+package basic
+
+import org.jetbrains.dokka.pages.ClasslikePageNode
+import org.jetbrains.dokka.pages.ModulePageNode
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+
+class DokkaBasicTests : AbstractCoreTest() {
+
+ @Test
+ fun basic1() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package basic
+ |
+ |class Test {
+ | val tI = 1
+ | fun tF() = 2
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = {
+ val root = it as ModulePageNode
+ assertTrue(root.getClasslikeToMemberMap().filterKeys { it.name == "Test" }.entries.firstOrNull()?.value?.size == 2)
+ }
+ }
+ }
+
+ private fun ModulePageNode.getClasslikeToMemberMap() =
+ this.parentMap.filterValues { it is ClasslikePageNode }.entries.groupBy({ it.value }) { it.key }
+}
diff --git a/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt b/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt
new file mode 100644
index 00000000..9d2c5825
--- /dev/null
+++ b/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt
@@ -0,0 +1,118 @@
+package basic
+
+import org.jetbrains.dokka.DokkaException
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jetbrains.dokka.utilities.DokkaConsoleLogger
+import org.jetbrains.dokka.utilities.DokkaLogger
+import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import testApi.logger.TestLogger
+
+class FailOnWarningTest : AbstractCoreTest() {
+
+ @Test
+ fun `throws exception if one or more warnings were emitted`() {
+ val configuration = dokkaConfiguration {
+ failOnWarning = true
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ }
+ }
+ }
+
+ assertThrows<DokkaException> {
+ testInline(
+ """
+ |/src/main/kotlin
+ |package sample
+ """.trimIndent(), configuration
+ ) {
+ pluginsSetupStage = {
+ logger.warn("Warning!")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `throws exception if one or more error were emitted`() {
+ val configuration = dokkaConfiguration {
+ failOnWarning = true
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ }
+ }
+ }
+
+ assertThrows<DokkaException> {
+ testInline(
+ """
+ |/src/main/kotlin
+ |package sample
+ """.trimIndent(), configuration
+ ) {
+ pluginsSetupStage = {
+ logger.error("Error!")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `does not throw if now warning or error was emitted`() {
+ logger = TestLogger(ZeroErrorOrWarningCountDokkaLogger())
+
+ val configuration = dokkaConfiguration {
+ failOnWarning = true
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ }
+ }
+ }
+
+
+ testInline(
+ """
+ |/src/main/kotlin
+ |package sample
+ """.trimIndent(), configuration
+ ) {
+ /* We expect no Exception */
+ }
+ }
+
+ @Test
+ fun `does not throw if disabled`() {
+ val configuration = dokkaConfiguration {
+ failOnWarning = false
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ }
+ }
+ }
+
+
+ testInline(
+ """
+ |/src/main/kotlin
+ |package sample
+ """.trimIndent(), configuration
+ ) {
+ pluginsSetupStage = {
+ logger.warn("Error!")
+ logger.error("Error!")
+ }
+ }
+ }
+}
+
+private class ZeroErrorOrWarningCountDokkaLogger(
+ logger: DokkaLogger = DokkaConsoleLogger
+) : DokkaLogger by logger {
+ override var warningsCount: Int = 0
+ override var errorsCount: Int = 0
+}
diff --git a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
new file mode 100644
index 00000000..bf78b847
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
@@ -0,0 +1,221 @@
+package content.annotations
+
+import matchers.content.*
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.PackagePageNode
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Test
+import utils.ParamAttributes
+import utils.bareSignature
+import utils.propertySignature
+
+
+class ContentForAnnotationsTest : AbstractCoreTest() {
+
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `function with documented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
+ | AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD
+ |)
+ |@Retention(AnnotationRetention.SOURCE)
+ |@MustBeDocumented
+ |annotation class Fancy
+ |
+ |
+ |@Fancy
+ |fun function(@Fancy abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ mapOf("Fancy" to emptySet()),
+ "",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(mapOf("Fancy" to emptySet()), emptySet(), "String")
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function with undocumented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
+ | AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD
+ |)
+ |@Retention(AnnotationRetention.SOURCE)
+ |annotation class Fancy
+ |
+ |@Fancy
+ |fun function(@Fancy abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `property with undocumented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@Suppress
+ |val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "", "", emptySet(), "val", "property", "Int")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `property with documented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@MustBeDocumented
+ |annotation class Fancy
+ |
+ |@Fancy
+ |val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(mapOf("Fancy" to emptySet()), "", "", emptySet(), "val", "property", "Int")
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `rich documented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@MustBeDocumented
+ |@Retention(AnnotationRetention.SOURCE)
+ |@Target(AnnotationTarget.FIELD)
+ |annotation class BugReport(
+ | val assignedTo: String = "[none]",
+ | val testCase: KClass<ABC> = ABC::class,
+ | val status: Status = Status.UNCONFIRMED,
+ | val ref: Reference = Reference(value = 1),
+ | val reportedBy: Array<Reference>,
+ | val showStopper: Boolean = false
+ |) {
+ | enum class Status {
+ | UNCONFIRMED, CONFIRMED, FIXED, NOTABUG
+ | }
+ | class ABC
+ |}
+ |annotation class Reference(val value: Int)
+ |
+ |
+ |@BugReport(
+ | assignedTo = "me",
+ | testCase = BugReport.ABC::class,
+ | status = BugReport.Status.FIXED,
+ | ref = Reference(value = 2),
+ | reportedBy = [Reference(value = 2), Reference(value = 4)],
+ | showStopper = true
+ |)
+ |val ltint: Int = 5
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(
+ mapOf(
+ "BugReport" to setOf(
+ "assignedTo",
+ "testCase",
+ "status",
+ "ref",
+ "reportedBy",
+ "showStopper"
+ )
+ ), "", "", emptySet(), "val", "ltint", "Int"
+ )
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt b/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt
new file mode 100644
index 00000000..69de1bcd
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt
@@ -0,0 +1,103 @@
+package content.annotations
+
+
+import matchers.content.*
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Test
+import utils.ParamAttributes
+import utils.bareSignature
+
+
+class DepredatedAndSinceKotlinTest : AbstractCoreTest() {
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `function with deprecated annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@Deprecated("And some things that should not have been forgotten were lost. History became legend. Legend became myth.")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "ring" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"ring" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "ring",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function with since kotlin annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@SinceKotlin("1.3")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "ring" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"ring" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "ring",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
new file mode 100644
index 00000000..a9689bc5
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt
@@ -0,0 +1,611 @@
+package content.params
+
+import matchers.content.*
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.model.DFunction
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.model.doc.Param
+import org.jetbrains.dokka.model.doc.Text
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.MemberPageNode
+import org.jetbrains.dokka.pages.dfs
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
+import org.junit.jupiter.api.Test
+import utils.*
+
+class ContentForParamsTest : AbstractCoreTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null, "abc" to ParamAttributes(
+ emptyMap(),
+ emptySet(),
+ "String"
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("comment to function")
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented parameter and other tags without function comment`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @author Kordyjan
+ | * @since 0.11
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ unnamedTag("Author") { +"Kordyjan" }
+ unnamedTag("Since") { +"0.11" }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented parameter and other tags`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @author Kordyjan
+ | * @since 0.11
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("comment to function")
+ unnamedTag("Author") { +"Kordyjan" }
+ unnamedTag("Since") { +"0.11" }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `single parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param abc comment to param
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("comment to function")
+ header(2) { +"Parameters" }
+ group {
+ platformHinted {
+ table {
+ group {
+ +"abc"
+ group { +"comment to param" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiple parameters`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @param second comment to second param
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("comment to function")
+ header(2) { +"Parameters" }
+ group {
+ platformHinted {
+ table {
+ group {
+ +"first"
+ group { +"comment to first param" }
+ }
+ group {
+ +"second"
+ group { +"comment to second param" }
+ }
+ group {
+ +"third"
+ group { +"comment to third param" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiple parameters without function description`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @param first comment to first param
+ | * @param second comment to second param
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ header(2) { +"Parameters" }
+ group {
+ platformHinted {
+ table {
+ group {
+ +"first"
+ group { +"comment to first param" }
+ }
+ group {
+ +"second"
+ group { +"comment to second param" }
+ }
+ group {
+ +"third"
+ group { +"comment to third param" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function with receiver`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param abc comment to param
+ | * @receiver comment to receiver
+ | */
+ |fun String.function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("comment to function")
+ header(2) { +"Parameters" }
+ group {
+ platformHinted {
+ table {
+ group {
+ +"<receiver>"
+ group { +"comment to receiver" }
+ }
+ group {
+ +"abc"
+ group { +"comment to param" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignatureWithReceiver(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "String",
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `missing parameter documentation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("comment to function")
+ header(2) { +"Parameters" }
+ group {
+ platformHinted {
+ table {
+ group {
+ +"first"
+ group { +"comment to first param" }
+ }
+ group {
+ +"third"
+ group { +"comment to third param" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `parameters mixed with other tags`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @author Kordyjan
+ | * @param second comment to second param
+ | * @since 0.11
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("comment to function")
+ unnamedTag("Author") { +"Kordyjan" }
+ unnamedTag("Since") { +"0.11" }
+ header(2) { +"Parameters" }
+
+ group {
+ platformHinted {
+ table {
+ group {
+ +"first"
+ group { +"comment to first param" }
+ }
+ group {
+ +"second"
+ group { +"comment to second param" }
+ }
+ group {
+ +"third"
+ group { +"comment to third param" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun javaDocCommentWithDocumentedParameters() {
+ testInline(
+ """
+ |/src/main/java/test/Main.java
+ |package test
+ | public class Main {
+ |
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @param second comment to second param
+ | */
+ | public void sample(String first, String second) {
+ |
+ | }
+ | }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val sampleFunction = module.dfs {
+ it is MemberPageNode && it.dri.first()
+ .toString() == "test/Main/sample/#java.lang.String#java.lang.String/PointingToDeclaration/"
+ } as MemberPageNode
+ val forJvm = (sampleFunction.documentable as DFunction).parameters.mapNotNull {
+ val jvm = it.documentation.keys.first { it.analysisPlatform == Platform.jvm }
+ it.documentation[jvm]
+ }
+
+ assert(forJvm.size == 2)
+ val (first, second) = forJvm.map { it.paramsDescription() }
+ assert(first == "comment to first param")
+ assert(second == "comment to second param")
+ }
+ }
+ }
+
+ private fun DocumentationNode.paramsDescription(): String =
+ children.firstIsInstanceOrNull<Param>()?.root?.children?.firstIsInstanceOrNull<Text>()?.body.orEmpty()
+
+}
diff --git a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
new file mode 100644
index 00000000..24970660
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
@@ -0,0 +1,459 @@
+package content.seealso
+
+import matchers.content.*
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Test
+import utils.ParamAttributes
+import utils.bareSignature
+import utils.pWrapped
+import utils.unnamedTag
+
+class ContentForSeeAlsoTest : AbstractCoreTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented seealso`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ header(2) { +"See also" }
+ group {
+ platformHinted {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ group { }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented seealso`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ header(2) { +"See also" }
+ group {
+ platformHinted {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ group { +"Comment to abc" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented seealso with stdlib link`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see Collection
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ header(2) { +"See also" }
+ group {
+ platformHinted {
+ table {
+ group {
+ //DRI should be "kotlin.collections/Collection////"
+ link { +"Collection" }
+ group { }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented seealso with stdlib link`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see Collection Comment to stdliblink
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ header(2) { +"See also" }
+ group {
+ platformHinted {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
+ group { +"Comment to stdliblink" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented seealso with stdlib link with other tags`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * random comment
+ | * @see Collection Comment to stdliblink
+ | * @author pikinier20
+ | * @since 0.11
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ pWrapped("random comment")
+ unnamedTag("Author") { +"pikinier20" }
+ unnamedTag("Since") { +"0.11" }
+
+ header(2) { +"See also" }
+ group {
+ platformHinted {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
+ group { +"Comment to stdliblink" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented multiple see also`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc1
+ | * @see abc Comment to abc2
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ header(2) { +"See also" }
+ group {
+ platformHinted {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ group { +"Comment to abc2" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented multiple see also mixed source`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc1
+ | * @see[Collection] Comment to collection
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ before {
+ header(2) { +"See also" }
+ group {
+ platformHinted {
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ group { +"Comment to abc1" }
+ }
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
+ group { +"Comment to collection" }
+ }
+ }
+ }
+ }
+ }
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt b/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt
new file mode 100644
index 00000000..cabe822d
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt
@@ -0,0 +1,456 @@
+package content.signatures
+
+import matchers.content.*
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.doc.Text
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Test
+import utils.ParamAttributes
+import utils.bareSignature
+import utils.propertySignature
+import utils.typealiasSignature
+
+class ContentForSignaturesTest : AbstractCoreTest() {
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ includeNonPublic = true
+ }
+ }
+ }
+
+ @Test
+ fun `function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `private function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |private fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "private",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `open function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |open fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "open",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function without parameters`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(): String {
+ | return "Hello"
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "function",
+ returnType = "String",
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `suspend function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |suspend fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ setOf("suspend"),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected open suspend function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected open suspend fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "protected",
+ "open",
+ setOf("suspend"),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected open suspend inline function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected open suspend inline fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "protected",
+ "open",
+ setOf("inline", "suspend"),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "", "", emptySet(), "val", "property", "Int")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `const property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |const val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "", "", setOf("const"), "val", "property", "Int")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "protected", "", emptySet(), "val", "property", "Int")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected lateinit property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected lateinit var property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "protected", "", setOf("lateinit"), "var", "property", "Int")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to String`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |typealias Alias = String
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "String")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to Int`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |typealias Alias = Int
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "Int")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to type in same package`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |typealias Alias = X
+ |class X
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "X")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to type in different package`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |import other.X
+ |typealias Alias = X
+ |
+ |/src/main/kotlin/test/source2.kt
+ |package other
+ |class X
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "other.X")
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt b/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt
new file mode 100644
index 00000000..7de48664
--- /dev/null
+++ b/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt
@@ -0,0 +1,254 @@
+package content.signatures
+
+import matchers.content.*
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Test
+import utils.functionSignature
+
+class ConstructorsSignaturesTest : AbstractCoreTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `class name without parenthesis`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class"
+ link { +"SomeClass" }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class name with empty parenthesis`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass()
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class"
+ link { +"SomeClass" }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class with a parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass(a: String)
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class"
+ link { +"SomeClass" }
+ +"(a:"
+ group { link { +"String" } }
+ +")"
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class with a val parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass(val a: String)
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class"
+ link { +"SomeClass" }
+ +"(a:" // TODO: Make sure if we still do not want to have "val" here
+ group { link { +"String" } }
+ +")"
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class with a parameterless secondary constructor`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass(a: String) {
+ | constructor()
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class"
+ link { +"SomeClass" }
+ +"(a:"
+ group { link { +"String" } }
+ +")"
+ }
+ }
+ }
+ group {
+ header { +"Constructors" }
+ table {
+ group {
+ link { +"<init>" }
+ functionSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "<init>"
+ )
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class with explicitly documented constructor`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ | /**
+ | * some comment
+ | * @constructor ctor comment
+ | **/
+ |class SomeClass(a: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ skipAllNotMatching()
+ group {
+ +"class"
+ link { +"SomeClass" }
+ +"(a:"
+ group { link { +"String" } }
+ +")"
+ }
+ }
+ }
+ group {
+ header { +"Constructors" }
+ table {
+ group {
+ link { +"<init>" }
+ platformHinted {
+ group {
+ group {
+ +"ctor comment"
+ }
+ }
+ group {
+ +"fun"
+ link { +"<init>" }
+ +"(a:"
+ group {
+ link { +"String" }
+ }
+ +")"
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/enums/EnumsTest.kt b/plugins/base/src/test/kotlin/enums/EnumsTest.kt
new file mode 100644
index 00000000..6a973f8e
--- /dev/null
+++ b/plugins/base/src/test/kotlin/enums/EnumsTest.kt
@@ -0,0 +1,234 @@
+package enums
+
+import matchers.content.*
+import org.jetbrains.dokka.model.ConstructorValues
+import org.jetbrains.dokka.model.DEnum
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Test
+
+class EnumsTest : AbstractCoreTest() {
+
+ @Test
+ fun basicEnum() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package enums
+ |
+ |enum class Test {
+ | E1,
+ | E2
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = {
+ val map = it.getClasslikeToMemberMap()
+ val test = map.filterKeys { it.name == "Test" }.values.firstOrNull()
+ assertTrue(test != null) { "Test not found" }
+ assertTrue(test!!.any { it.name == "E1" } && test.any { it.name == "E2" }) { "Enum entries missing in parent" }
+ }
+ }
+ }
+
+ @Test
+ fun enumWithCompanion() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package enums
+ |
+ |enum class Test {
+ | E1,
+ | E2;
+ | companion object {}
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesTransformationStage = { m ->
+ m.packages.let { p ->
+ assertTrue(p.isNotEmpty(), "Package list cannot be empty")
+ p.first().classlikes.let { c ->
+ assertTrue(c.isNotEmpty(), "Classlikes list cannot be empty")
+
+ val enum = c.first() as DEnum
+ assertEquals(enum.name, "Test")
+ assertEquals(enum.entries.count(), 2)
+ assertNotNull(enum.companion)
+ }
+ }
+ }
+ pagesGenerationStage = { module ->
+ val map = module.getClasslikeToMemberMap()
+ val test = map.filterKeys { it.name == "Test" }.values.firstOrNull()
+ assertNotNull(test, "Test not found")
+ assertTrue(test!!.any { it.name == "E1" } && test.any { it.name == "E2" }) { "Enum entries missing in parent" }
+ }
+ }
+ }
+
+ @Test
+ fun enumWithConstructor() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package enums
+ |
+ |
+ |enum class Test(name: String, index: Int, excluded: Boolean) {
+ | E1("e1", 1, true),
+ | E2("e2", 2, false);
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesTransformationStage = { m ->
+ m.packages.let { p ->
+ p.first().classlikes.let { c ->
+ val enum = c.first() as DEnum
+ val (first, second) = enum.entries
+
+ assertEquals(1, first.extra.allOfType<ConstructorValues>().size)
+ assertEquals(1, second.extra.allOfType<ConstructorValues>().size)
+ assertEquals(listOf("\"e1\"", "1", "true"), first.extra.allOfType<ConstructorValues>().first().values.values.first())
+ assertEquals(listOf("\"e2\"", "2", "false"), second.extra.allOfType<ConstructorValues>().first().values.values.first())
+ }
+ }
+ }
+ pagesGenerationStage = { module ->
+ val entryPage = module.dfs { it.name == "E1" } as ClasslikePageNode
+ val signaturePart = (entryPage.content.dfs {
+ it is ContentGroup && it.dci.toString() == "[enums/Test.E1///PointingToDeclaration/][Symbol]"
+ } as ContentGroup)
+ assertEquals("(\"e1\", 1, true)", signaturePart.constructorSignature())
+ }
+ }
+ }
+
+ @Test
+ fun enumWithMethods() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package enums
+ |
+ |
+ |interface Sample {
+ | fun toBeImplemented(): String
+ |}
+ |
+ |enum class Test: Sample {
+ | E1 {
+ | override fun toBeImplemented(): String = "e1"
+ | }
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesTransformationStage = { m ->
+ m.packages.let { p ->
+ p.first().classlikes.let { c ->
+ val enum = c.first { it is DEnum } as DEnum
+ val first = enum.entries.first()
+
+ assertEquals(1, first.extra.allOfType<ConstructorValues>().size)
+ assertEquals(emptyList<String>(), first.extra.allOfType<ConstructorValues>().first().values.values.first())
+ assertNotNull(first.functions.find { it.name == "toBeImplemented" })
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun enumWithAnnotationsOnEntries(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package enums
+ |
+ |enum class Test {
+ | /**
+ | Sample docs for E1
+ | **/
+ | @SinceKotlin("1.3") // This annotation is transparent due to lack of @MustBeDocumented annotation
+ | E1
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = { m ->
+ val entryNode = m.children.first { it.name == "enums" }.children.first { it.name == "Test" }.children.first() as ClasslikePageNode
+ val signature = (entryNode.content as ContentGroup).dfs { it is ContentGroup && it.dci.toString() == "[enums/Test.E1///PointingToDeclaration/][Cover]" } as ContentGroup
+
+ signature.assertNode {
+ header(1) { +"E1" }
+ platformHinted {
+ group {
+ group {
+ + "Sample docs for E1"
+ }
+ }
+ group {
+ group {
+ link { +"E1" }
+ +"()"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ fun RootPageNode.getClasslikeToMemberMap() =
+ this.parentMap.filterValues { it is ClasslikePageNode }.entries.groupBy({ it.value }) { it.key }
+
+ private fun ContentGroup.constructorSignature(): String =
+ (children.single() as ContentGroup).children.drop(1).joinToString(separator = "") { (it as ContentText).text }
+}
diff --git a/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt b/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt
new file mode 100644
index 00000000..4dfdc410
--- /dev/null
+++ b/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt
@@ -0,0 +1,104 @@
+package expect
+
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.assertTrue
+import java.nio.file.Files
+import java.nio.file.Path
+import java.nio.file.Paths
+import java.util.concurrent.TimeUnit
+
+abstract class AbstractExpectTest(
+ val testDir: Path? = Paths.get("src/test", "resources", "expect"),
+ val formats: List<String> = listOf("html")
+) : AbstractCoreTest() {
+
+ protected fun generateOutput(path: Path, outFormat: String): Path? {
+ val config = dokkaConfiguration {
+ format = outFormat
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf(path.toAbsolutePath().asString())
+ }
+ }
+ }
+
+ var result: Path? = null
+ testFromData(config, cleanupOutput = false) {
+ renderingStage = { _, context -> result = Paths.get(context.configuration.outputDir) }
+ }
+ return result
+ }
+
+ protected fun compareOutput(expected: Path, obtained: Path?, gitTimeout: Long = 500) {
+ obtained?.let { path ->
+ val gitCompare = ProcessBuilder(
+ "git",
+ "--no-pager",
+ "diff",
+ expected.asString(),
+ path.asString()
+ ).also { logger.info("git diff command: ${it.command().joinToString(" ")}") }
+ .also { it.redirectErrorStream() }.start()
+
+ assertTrue(gitCompare.waitFor(gitTimeout, TimeUnit.MILLISECONDS)) { "Git timed out after $gitTimeout" }
+ gitCompare.inputStream.bufferedReader().lines().forEach { logger.info(it) }
+ assertTrue(gitCompare.exitValue() == 0) { "${path.fileName}: outputs don't match" }
+ } ?: throw AssertionError("obtained path is null")
+ }
+
+ protected fun compareOutputWithExcludes(
+ expected: Path,
+ obtained: Path?,
+ excludes: List<String>,
+ timeout: Long = 500
+ ) {
+ obtained?.let { path ->
+ val (res, out, err) = runDiff(expected, obtained, excludes, timeout)
+ assertTrue(res == 0, "Outputs differ:\nstdout - $out\n\nstderr - ${err ?: ""}")
+ } ?: throw AssertionError("obtained path is null")
+ }
+
+ protected fun runDiff(exp: Path, obt: Path, excludes: List<String>, timeout: Long): ProcessResult =
+ ProcessBuilder().command(
+ listOf("diff", "-ru") + excludes.flatMap { listOf("-x", it) } + listOf("--", exp.asString(), obt.asString())
+ ).also {
+ it.redirectErrorStream()
+ }.start().also { assertTrue(it.waitFor(timeout, TimeUnit.MILLISECONDS), "diff timed out") }.let {
+ ProcessResult(it.exitValue(), it.inputStream.bufferResult())
+ }
+
+
+ protected fun testOutput(p: Path, outFormat: String) {
+ val expectOut = p.resolve("out/$outFormat")
+ val testOut = generateOutput(p.resolve("src"), outFormat)
+ .also { logger.info("Test out: ${it?.asString()}") }
+
+ compareOutput(expectOut.toAbsolutePath(), testOut?.toAbsolutePath())
+ testOut?.deleteRecursively()
+ }
+
+ protected fun testOutputWithExcludes(
+ p: Path,
+ outFormat: String,
+ ignores: List<String> = emptyList(),
+ timeout: Long = 500
+ ) {
+ val expected = p.resolve("out/$outFormat")
+ generateOutput(p.resolve("src"), outFormat)
+ ?.let { obtained ->
+ compareOutputWithExcludes(expected, obtained, ignores, timeout)
+
+ obtained.deleteRecursively()
+ } ?: throw AssertionError("Output not generated for ${p.fileName}")
+ }
+
+ protected fun generateExpect(p: Path, outFormat: String) {
+ val out = p.resolve("out/$outFormat/")
+ Files.createDirectories(out)
+
+ val ret = generateOutput(p.resolve("src"), outFormat)
+ Files.list(out).forEach { it.deleteRecursively() }
+ ret?.let { Files.list(it).forEach { f -> f.copyRecursively(out.resolve(f.fileName)) } }
+ }
+
+}
diff --git a/plugins/base/src/test/kotlin/expect/ExpectGenerator.kt b/plugins/base/src/test/kotlin/expect/ExpectGenerator.kt
new file mode 100644
index 00000000..cb3313f9
--- /dev/null
+++ b/plugins/base/src/test/kotlin/expect/ExpectGenerator.kt
@@ -0,0 +1,13 @@
+package expect
+
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+
+class ExpectGenerator : AbstractExpectTest() {
+
+ @Disabled
+ @Test
+ fun generateAll() = testDir?.dirsWithFormats(formats).orEmpty().forEach { (p, f) ->
+ generateExpect(p, f)
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/expect/ExpectTest.kt b/plugins/base/src/test/kotlin/expect/ExpectTest.kt
new file mode 100644
index 00000000..bed841a6
--- /dev/null
+++ b/plugins/base/src/test/kotlin/expect/ExpectTest.kt
@@ -0,0 +1,24 @@
+package expect
+
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.DynamicTest.dynamicTest
+import org.junit.jupiter.api.TestFactory
+
+class ExpectTest : AbstractExpectTest() {
+ private val ignores: List<String> = listOf(
+ "images",
+ "scripts",
+ "images",
+ "styles",
+ "*.js",
+ "*.css",
+ "*.svg",
+ "*.map"
+ )
+
+ @Disabled
+ @TestFactory
+ fun expectTest() = testDir?.dirsWithFormats(formats).orEmpty().map { (p, f) ->
+ dynamicTest("${p.fileName}-$f") { testOutputWithExcludes(p, f, ignores) }
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/expect/ExpectUtils.kt b/plugins/base/src/test/kotlin/expect/ExpectUtils.kt
new file mode 100644
index 00000000..1f54c20e
--- /dev/null
+++ b/plugins/base/src/test/kotlin/expect/ExpectUtils.kt
@@ -0,0 +1,28 @@
+package expect
+
+import java.io.InputStream
+import java.nio.file.Files
+import java.nio.file.Path
+import kotlin.streams.toList
+
+data class ProcessResult(val code: Int, val out: String, val err: String? = null)
+
+internal fun Path.dirsWithFormats(formats: List<String>): List<Pair<Path, String>> =
+ Files.list(this).toList().filter { Files.isDirectory(it) }.flatMap { p -> formats.map { p to it } }
+
+internal fun Path.asString() = normalize().toString()
+internal fun Path.deleteRecursively() = toFile().deleteRecursively()
+
+internal fun Path.copyRecursively(target: Path) = toFile().copyRecursively(target.toFile())
+
+internal fun Path.listRecursively(filter: (Path) -> Boolean): List<Path> = when {
+ Files.isDirectory(this) -> listOfNotNull(takeIf(filter)) + Files.list(this).toList().flatMap {
+ it.listRecursively(
+ filter
+ )
+ }
+ Files.isRegularFile(this) -> listOfNotNull(this.takeIf(filter))
+ else -> emptyList()
+ }
+
+internal fun InputStream.bufferResult(): String = this.bufferedReader().lines().toList().joinToString("\n") \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt b/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt
new file mode 100644
index 00000000..c8b9f2d4
--- /dev/null
+++ b/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt
@@ -0,0 +1,173 @@
+package filter
+
+import org.jetbrains.dokka.PackageOptionsImpl
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class DeprecationFilterTest : AbstractCoreTest() {
+ @Test
+ fun `function with false global skipDeprecated`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ skipDeprecated = false
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |fun testFunction() { }
+ |
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 1
+ )
+ }
+ }
+ }
+ @Test
+ fun `deprecated function with false global skipDeprecated`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ skipDeprecated = false
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |@Deprecated("dep")
+ |fun testFunction() { }
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 1
+ )
+ }
+ }
+ }
+ @Test
+ fun `deprecated function with true global skipDeprecated`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ skipDeprecated = true
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |@Deprecated("dep")
+ |fun testFunction() { }
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 0
+ )
+ }
+ }
+ }
+ @Test
+ fun `deprecated function with false global true package skipDeprecated`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ skipDeprecated = false
+ perPackageOptions = mutableListOf(
+ PackageOptionsImpl("example",
+ true,
+ false,
+ true,
+ false)
+ )
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |@Deprecated("dep")
+ |fun testFunction() { }
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 0
+ )
+ }
+ }
+ }
+ @Test
+ fun `deprecated function with true global false package skipDeprecated`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ skipDeprecated = true
+ perPackageOptions = mutableListOf(
+ PackageOptionsImpl("example",
+ false,
+ false,
+ false,
+ false)
+ )
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |@Deprecated("dep")
+ |fun testFunction() { }
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 1
+ )
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/filter/EmptyPackagesFilterTest.kt b/plugins/base/src/test/kotlin/filter/EmptyPackagesFilterTest.kt
new file mode 100644
index 00000000..e5b9e9c2
--- /dev/null
+++ b/plugins/base/src/test/kotlin/filter/EmptyPackagesFilterTest.kt
@@ -0,0 +1,63 @@
+package filter
+
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class EmptyPackagesFilterTest : AbstractCoreTest() {
+ @Test
+ fun `empty package with false skipEmptyPackages`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ skipEmptyPackages = false
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.isNotEmpty()
+ )
+ }
+ }
+ }
+ @Test
+ fun `empty package with true skipEmptyPackages`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ skipEmptyPackages = true
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.isEmpty()
+ )
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/filter/VisibilityFilterTest.kt b/plugins/base/src/test/kotlin/filter/VisibilityFilterTest.kt
new file mode 100644
index 00000000..192de449
--- /dev/null
+++ b/plugins/base/src/test/kotlin/filter/VisibilityFilterTest.kt
@@ -0,0 +1,173 @@
+package filter
+
+import org.jetbrains.dokka.PackageOptionsImpl
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class VisibilityFilterTest : AbstractCoreTest() {
+ @Test
+ fun `public function with false global includeNonPublic`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ includeNonPublic = false
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |fun testFunction() { }
+ |
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 1
+ )
+ }
+ }
+ }
+ @Test
+ fun `private function with false global includeNonPublic`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ includeNonPublic = false
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |private fun testFunction() { }
+ |
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 0
+ )
+ }
+ }
+ }
+ @Test
+ fun `private function with true global includeNonPublic`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ includeNonPublic = true
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |private fun testFunction() { }
+ |
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 1
+ )
+ }
+ }
+ }
+ @Test
+ fun `private function with false global true package includeNonPublic`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ includeNonPublic = false
+ perPackageOptions = mutableListOf(
+ PackageOptionsImpl("example",
+ true,
+ false,
+ false,
+ false)
+ )
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |private fun testFunction() { }
+ |
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 1
+ )
+ }
+ }
+ }
+ @Test
+ fun `private function with true global false package includeNonPublic`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/basic/Test.kt")
+ includeNonPublic = true
+ perPackageOptions = mutableListOf(
+ PackageOptionsImpl("example",
+ false,
+ false,
+ false,
+ false)
+ )
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |package example
+ |
+ |private fun testFunction() { }
+ |
+ |
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesFirstTransformationStep = {
+ Assertions.assertTrue(
+ it.component2().packages.first().functions.size == 0
+ )
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/issues/IssuesTest.kt b/plugins/base/src/test/kotlin/issues/IssuesTest.kt
new file mode 100644
index 00000000..7b065349
--- /dev/null
+++ b/plugins/base/src/test/kotlin/issues/IssuesTest.kt
@@ -0,0 +1,73 @@
+package issues
+
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.DFunction
+import org.junit.jupiter.api.Test
+import utils.AbstractModelTest
+import utils.name
+
+class IssuesTest : AbstractModelTest("/src/main/kotlin/issues/Test.kt", "issues") {
+
+ @Test
+ fun errorClasses() {
+ inlineModelTest(
+ """
+ |class Test(var value: String) {
+ | fun test(): List<String> = emptyList()
+ | fun brokenApply(v: String) = apply { value = v }
+ |
+ | fun brokenRun(v: String) = run {
+ | value = v
+ | this
+ | }
+ |
+ | fun brokenLet(v: String) = let {
+ | it.value = v
+ | it
+ | }
+ |
+ | fun brokenGenerics() = listOf("a", "b", "c")
+ |
+ | fun working(v: String) = doSomething()
+ |
+ | fun doSomething(): String = "Hello"
+ |}
+ """,
+ configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+ ) {
+ with((this / "issues" / "Test").cast<DClass>()) {
+ (this / "working").cast<DFunction>().type.name equals "String"
+ (this / "doSomething").cast<DFunction>().type.name equals "String"
+ (this / "brokenGenerics").cast<DFunction>().type.name equals "List"
+ (this / "brokenApply").cast<DFunction>().type.name equals "Test"
+ (this / "brokenRun").cast<DFunction>().type.name equals "Test"
+ (this / "brokenLet").cast<DFunction>().type.name equals "Test"
+ }
+ }
+ }
+
+ //@Test
+ // fun errorClasses() {
+ // checkSourceExistsAndVerifyModel("testdata/issues/errorClasses.kt",
+ // modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true, withKotlinRuntime = true)) { model ->
+ // val cls = model.members.single().members.single()
+ //
+ // fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name
+ // assertEquals("Test", cls.members[1].returnType())
+ // assertEquals("Test", cls.members[2].returnType())
+ // assertEquals("Test", cls.members[3].returnType())
+ // assertEquals("List", cls.members[4].returnType())
+ // assertEquals("String", cls.members[5].returnType())
+ // assertEquals("String", cls.members[6].returnType())
+ // assertEquals("String", cls.members[7].returnType())
+ // }
+ // }
+
+}
diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
new file mode 100644
index 00000000..a8fc9f6d
--- /dev/null
+++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt
@@ -0,0 +1,225 @@
+package linkableContent
+
+import org.jetbrains.dokka.SourceLinkDefinitionImpl
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer
+import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer
+import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.plugability.plugin
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jetbrains.kotlin.utils.addToStdlib.cast
+import org.jetbrains.kotlin.utils.addToStdlib.safeAs
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+import java.nio.file.Paths
+
+class LinkableContentTest : AbstractCoreTest() {
+
+ @Test
+ fun `Include module and package documentation`() {
+
+ val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath()
+ val includesDir = getTestDataDir("linkable/includes").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ moduleName = "example"
+ analysisPlatform = "js"
+ sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ name = "js"
+ includes = listOf(Paths.get("$includesDir/include2.md").toString())
+ }
+ sourceSet {
+ moduleName = "example"
+ analysisPlatform = "jvm"
+ sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ name = "jvm"
+ includes = listOf(Paths.get("$includesDir/include1.md").toString())
+ }
+ }
+ }
+
+ testFromData(configuration) {
+ documentablesMergingStage = {
+ Assertions.assertEquals(2, it.documentation.size)
+ Assertions.assertEquals(2, it.packages.size)
+ Assertions.assertEquals(1, it.packages.first().documentation.size)
+ Assertions.assertEquals(1, it.packages.last().documentation.size)
+ }
+ }
+
+ }
+
+ @Test
+ fun `Sources multiplatform class documentation`() {
+
+ val testDataDir = getTestDataDir("linkable/sources").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ moduleName = "example"
+ analysisPlatform = "js"
+ sourceRoots = listOf("$testDataDir/jsMain/kotlin")
+ sourceLinks = listOf(
+ SourceLinkDefinitionImpl(
+ path = "jsMain/kotlin",
+ url = "https://github.com/user/repo/tree/master/src/jsMain/kotlin",
+ lineSuffix = "#L"
+ )
+ )
+ name = "js"
+ }
+ sourceSet {
+ moduleName = "example"
+ analysisPlatform = "jvm"
+ sourceRoots = listOf("$testDataDir/jvmMain/kotlin")
+ sourceLinks = listOf(
+ SourceLinkDefinitionImpl(
+ path = "jvmMain/kotlin",
+ url = "https://github.com/user/repo/tree/master/src/jvmMain/kotlin",
+ lineSuffix = "#L"
+ )
+ )
+ name = "jvm"
+ }
+ }
+ }
+
+ testFromData(configuration) {
+ renderingStage = { rootPageNode, dokkaContext ->
+ val newRoot = SourceLinksTransformer(
+ dokkaContext,
+ PageContentBuilder(
+ dokkaContext.single(dokkaContext.plugin<DokkaBase>().commentsToContentConverter),
+ dokkaContext.single(dokkaContext.plugin<DokkaBase>().signatureProvider),
+ dokkaContext.logger
+ )
+ ).invoke(rootPageNode)
+ val moduleChildren = newRoot.children
+ Assertions.assertEquals(1, moduleChildren.size)
+ val packageChildren = moduleChildren.first().children
+ Assertions.assertEquals(2, packageChildren.size)
+ packageChildren.forEach {
+ val name = it.name.substringBefore("Class")
+ val crl = it.safeAs<ClasslikePageNode>()?.content?.safeAs<ContentGroup>()?.children?.last()
+ ?.safeAs<ContentGroup>()?.children?.last()?.safeAs<ContentGroup>()?.children?.lastOrNull()
+ ?.safeAs<ContentTable>()?.children?.singleOrNull()
+ ?.safeAs<ContentGroup>()?.children?.singleOrNull().safeAs<ContentResolvedLink>()
+ Assertions.assertEquals(
+ "https://github.com/user/repo/tree/master/src/${name.toLowerCase()}Main/kotlin/${name}Class.kt#L3",
+ crl?.address
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `Samples multiplatform documentation`() {
+
+ val testDataDir = getTestDataDir("linkable/samples").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ moduleName = "example"
+ analysisPlatform = "js"
+ sourceRoots = listOf("$testDataDir/jsMain/kotlin")
+ name = "js"
+ samples = listOf("$testDataDir/jsMain/resources/Samples.kt")
+ }
+ sourceSet {
+ moduleName = "example"
+ analysisPlatform = "jvm"
+ sourceRoots = listOf("$testDataDir/jvmMain/kotlin")
+ name = "jvm"
+ samples = listOf("$testDataDir/jvmMain/resources/Samples.kt")
+ }
+ }
+ }
+
+ testFromData(configuration) {
+ renderingStage = { rootPageNode, dokkaContext ->
+ val newRoot = DefaultSamplesTransformer(dokkaContext).invoke(rootPageNode)
+
+ val moduleChildren = newRoot.children
+ Assertions.assertEquals(1, moduleChildren.size)
+ val packageChildren = moduleChildren.first().children
+ Assertions.assertEquals(2, packageChildren.size)
+ packageChildren.forEach {
+ val name = it.name.substringBefore("Class")
+ val classChildren = it.children
+ Assertions.assertEquals(2, classChildren.size)
+ val function = classChildren.find { it.name == "printWithExclamation" }
+ val text = function.cast<MemberPageNode>().content.cast<ContentGroup>().children.last()
+ .cast<ContentDivergentGroup>().children.single()
+ .cast<ContentDivergentInstance>().before
+ .cast<ContentGroup>().children.last()
+ .cast<ContentGroup>().children.last()
+ .cast<PlatformHintedContent>().children.single()
+ .cast<ContentGroup>().children.single()
+ .cast<ContentGroup>().children.single()
+ .cast<ContentCodeBlock>().children.single().cast<ContentText>().text
+ Assertions.assertEquals(
+ """|import p2.${name}Class
+ |fun main() {
+ | //sampleStart
+ | ${name}Class().printWithExclamation("Hi, $name")
+ | //sampleEnd
+ |}""".trimMargin(),
+ text
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `Documenting return type for a function in inner class with generic parent`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class Sample<S>(first: S){
+ | inner class SampleInner {
+ | fun foo(): S = TODO()
+ | }
+ |}
+ |
+ """.trimIndent(),
+ dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ name = "js"
+ }
+ }
+ }
+ ) {
+ renderingStage = { module, _ ->
+ val sample = module.children.single { it.name == "test" }
+ .children.single { it.name == "Sample" }.cast<ClasslikePageNode>()
+ val foo = sample
+ .children.single { it.name == "SampleInner" }.cast<ClasslikePageNode>()
+ .children.single { it.name == "foo" }.cast<MemberPageNode>()
+
+ val returnTypeNode = foo.content.dfs {
+ val link = it.safeAs<ContentDRILink>()?.children
+ val child = link?.first().safeAs<ContentText>()
+ child?.text == "S"
+ }?.safeAs<ContentDRILink>()
+
+ Assertions.assertEquals(sample.dri.first(), returnTypeNode?.address)
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/locationProvider/DefaultLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/DefaultLocationProviderTest.kt
new file mode 100644
index 00000000..a219fb04
--- /dev/null
+++ b/plugins/base/src/test/kotlin/locationProvider/DefaultLocationProviderTest.kt
@@ -0,0 +1,41 @@
+package locationProvider
+
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProvider
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.assertNotEquals
+import org.junit.jupiter.api.Test
+
+class DefaultLocationProviderTest: AbstractCoreTest() {
+ @Test
+ fun `#644 same directory for module and package`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/basic/Test.kt
+ |
+ |class Test {
+ | val x = 1
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ var context: DokkaContext? = null
+ pluginsSetupStage = {
+ context = it
+ }
+
+ pagesGenerationStage = { module ->
+ val lp = DefaultLocationProvider(module, context!!)
+ assertNotEquals(lp.resolve(module.children.single()).removePrefix("/"), lp.resolve(module))
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/markdown/KDocTest.kt b/plugins/base/src/test/kotlin/markdown/KDocTest.kt
new file mode 100644
index 00000000..f5b29322
--- /dev/null
+++ b/plugins/base/src/test/kotlin/markdown/KDocTest.kt
@@ -0,0 +1,47 @@
+package markdown
+
+import org.jetbrains.dokka.model.DPackage
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.pages.ModulePageNode
+import org.junit.jupiter.api.Assertions.*
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+
+abstract class KDocTest : AbstractCoreTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/example/Test.kt")
+ }
+ }
+ }
+
+ private fun interpolateKdoc(kdoc: String) = """
+ |/src/main/kotlin/example/Test.kt
+ |package example
+ | /**
+ ${kdoc.split("\n").joinToString("") { "| *$it\n" } }
+ | */
+ |class Test
+ """.trimMargin()
+
+ private fun actualDocumentationNode(modulePageNode: ModulePageNode) =
+ (modulePageNode.documentable?.children?.first() as DPackage)
+ .classlikes.single()
+ .documentation.values.single()
+
+
+ protected fun executeTest(kdoc: String, expectedDocumentationNode: DocumentationNode) {
+ testInline(
+ interpolateKdoc(kdoc),
+ configuration
+ ) {
+ pagesGenerationStage = {
+ assertEquals(
+ expectedDocumentationNode,
+ actualDocumentationNode(it as ModulePageNode)
+ )
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/markdown/LinkTest.kt b/plugins/base/src/test/kotlin/markdown/LinkTest.kt
new file mode 100644
index 00000000..a6333c5a
--- /dev/null
+++ b/plugins/base/src/test/kotlin/markdown/LinkTest.kt
@@ -0,0 +1,79 @@
+package markdown
+
+import org.jetbrains.dokka.pages.ClasslikePageNode
+import org.jetbrains.dokka.pages.ContentDRILink
+import org.jetbrains.dokka.pages.MemberPageNode
+import org.jetbrains.dokka.pages.dfs
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Assertions.assertNotNull
+import org.junit.jupiter.api.Test
+
+class LinkTest : AbstractCoreTest() {
+ @Test
+ fun linkToClassLoader() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/parser")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/parser/Test.kt
+ |package parser
+ |
+ | /**
+ | * Some docs that link to [ClassLoader.clearAssertionStatus]
+ | */
+ |fun test(x: ClassLoader) = x.clearAssertionStatus()
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ renderingStage = { rootPageNode, _ ->
+ assertNotNull((rootPageNode.children.single().children.single() as MemberPageNode)
+ .content
+ .dfs { node ->
+ node is ContentDRILink &&
+ node.address.toString() == "parser//test/#java.lang.ClassLoader/PointingToDeclaration/"}
+ )
+ }
+ }
+ }
+
+ @Test
+ fun returnTypeShouldHaveLinkToOuterClassFromInner() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ displayName = "JVM"
+ }
+ }
+ }
+ //This does not contain a package to check for situation when the package has to be artificially generated
+ testInline(
+ """
+ |/src/main/kotlin/parser/Test.kt
+ |
+ |class Outer<OUTER> {
+ | inner class Inner<INNER> {
+ | fun foo(): OUTER = TODO()
+ | }
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ renderingStage = { rootPageNode, _ ->
+ val root = rootPageNode.children.single().children.single() as ClasslikePageNode
+ val innerClass = root.children.first { it is ClasslikePageNode }
+ val foo = innerClass.children.first { it.name == "foo" } as MemberPageNode
+
+ assertEquals(root.dri.first().toString(), "[JVM root]/Outer///PointingToDeclaration/")
+ assertNotNull(foo.content.dfs { it is ContentDRILink && it.address.toString() == root.dri.first().toString() } )
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/markdown/ParserTest.kt b/plugins/base/src/test/kotlin/markdown/ParserTest.kt
new file mode 100644
index 00000000..ee6170c2
--- /dev/null
+++ b/plugins/base/src/test/kotlin/markdown/ParserTest.kt
@@ -0,0 +1,1123 @@
+package org.jetbrains.dokka.tests
+
+import markdown.KDocTest
+import org.jetbrains.dokka.model.doc.*
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+
+
+class ParserTest : KDocTest() {
+
+ @Test
+ fun `Simple text`() {
+ val kdoc = """
+ | This is simple test of string
+ | Next line
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text("This is simple test of string Next line")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Simple text with new line`() {
+ val kdoc = """
+ | This is simple test of string\
+ | Next line
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Text("This is simple test of string"),
+ Br,
+ Text("Next line")
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Text with Bold and Emphasis decorators`() {
+ val kdoc = """
+ | This is **simple** test of _string_
+ | Next **_line_**
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Text("This is "),
+ B(listOf(Text("simple"))),
+ Text(" test of "),
+ I(listOf(Text("string"))),
+ Text(" Next "),
+ B(listOf(I(listOf(Text("line")))))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Text with Colon`() {
+ val kdoc = """
+ | This is simple text with: colon!
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text("This is simple text with: colon!")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Multilined text`() {
+ val kdoc = """
+ | Text
+ | and
+ | String
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text("Text and String")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Paragraphs`() {
+ val kdoc = """
+ | Paragraph number
+ | one
+ |
+ | Paragraph\
+ | number two
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ P(listOf(Text("Paragraph number one"))),
+ P(listOf(Text("Paragraph"), Br, Text("number two")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Emphasis with star`() {
+ val kdoc = " *text*"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(I(listOf(Text("text")))))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Underscores that are not Emphasis`() {
+ val kdoc = "text_with_underscores"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text("text_with_underscores")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Emphasis with underscores`() {
+ val kdoc = "_text_"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(I(listOf(Text("text")))))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Embedded star`() {
+ val kdoc = "Embedded*Star"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(listOf(Text("Embedded*Star")))
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+
+ @Test
+ fun `Unordered list`() {
+ val kdoc = """
+ | * list item 1
+ | * list item 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("list item 1"))))),
+ Li(listOf(P(listOf(Text("list item 2")))))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Unordered list with multilines`() {
+ val kdoc = """
+ | * list item 1
+ | continue 1
+ | * list item 2\
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("list item 1 continue 1"))))),
+ Li(listOf(P(listOf(Text("list item 2"), Br, Text("continue 2")))))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Unordered list with Bold`() {
+ val kdoc = """
+ | * list **item** 1
+ | continue 1
+ | * list __item__ 2
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ul(
+ listOf(
+ Li(
+ listOf(
+ P(
+ listOf(
+ Text("list "),
+ B(listOf(Text("item"))),
+ Text(" 1 continue 1")
+ )
+ )
+ )
+ ),
+ Li(
+ listOf(
+ P(
+ listOf(
+ Text("list "),
+ B(listOf(Text("item"))),
+ Text(" 2 continue 2")
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Unordered list with nested bullets`() {
+ val kdoc = """
+ | * Outer first
+ | Outer next line
+ | * Outer second
+ | - Middle first
+ | Middle next line
+ | - Middle second
+ | + Inner first
+ | Inner next line
+ | - Middle third
+ | * Outer third
+ |
+ | New paragraph""".trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("Outer first Outer next line"))))),
+ Li(listOf(P(listOf(Text("Outer second"))))),
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("Middle first Middle next line"))))),
+ Li(listOf(P(listOf(Text("Middle second"))))),
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("Inner first Inner next line")))))
+ )
+ ),
+ Li(listOf(P(listOf(Text("Middle third")))))
+ )
+ ),
+ Li(listOf(P(listOf(Text("Outer third")))))
+ )
+ ),
+ P(listOf(Text("New paragraph")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Ordered list`() {
+ val kdoc = """
+ | 1. list item 1
+ | 2. list item 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("list item 1"))))),
+ Li(listOf(P(listOf(Text("list item 2")))))
+ ),
+ mapOf("start" to "1")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+
+ @Test
+ fun `Ordered list beginning from other number`() {
+ val kdoc = """
+ | 9. list item 1
+ | 12. list item 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("list item 1"))))),
+ Li(listOf(P(listOf(Text("list item 2")))))
+ ),
+ mapOf("start" to "9")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Ordered list with multilines`() {
+ val kdoc = """
+ | 2. list item 1
+ | continue 1
+ | 3. list item 2
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("list item 1 continue 1"))))),
+ Li(listOf(P(listOf(Text("list item 2 continue 2")))))
+ ),
+ mapOf("start" to "2")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Ordered list with Bold`() {
+ val kdoc = """
+ | 1. list **item** 1
+ | continue 1
+ | 2. list __item__ 2
+ | continue 2
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ Ol(
+ listOf(
+ Li(
+ listOf(
+ P(
+ listOf(
+ Text("list "),
+ B(listOf(Text("item"))),
+ Text(" 1 continue 1")
+ )
+ )
+ )
+ ),
+ Li(
+ listOf(
+ P(
+ listOf(
+ Text("list "),
+ B(listOf(Text("item"))),
+ Text(" 2 continue 2")
+ )
+ )
+ )
+ )
+ ),
+ mapOf("start" to "1")
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Ordered list with nested bullets`() {
+ val kdoc = """
+ | 1. Outer first
+ | Outer next line
+ | 2. Outer second
+ | 1. Middle first
+ | Middle next line
+ | 2. Middle second
+ | 1. Inner first
+ | Inner next line
+ | 5. Middle third
+ | 4. Outer third
+ |
+ | New paragraph""".trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Outer first Outer next line"))))),
+ Li(listOf(P(listOf(Text("Outer second"))))),
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Middle first Middle next line"))))),
+ Li(listOf(P(listOf(Text("Middle second"))))),
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Inner first Inner next line")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text("Middle third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text("Outer third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ P(listOf(Text("New paragraph")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Ordered nested in Unordered nested in Ordered list`() {
+ val kdoc = """
+ | 1. Outer first
+ | Outer next line
+ | 2. Outer second
+ | + Middle first
+ | Middle next line
+ | + Middle second
+ | 1. Inner first
+ | Inner next line
+ | + Middle third
+ | 4. Outer third
+ |
+ | New paragraph""".trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Outer first Outer next line"))))),
+ Li(listOf(P(listOf(Text("Outer second"))))),
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("Middle first Middle next line"))))),
+ Li(listOf(P(listOf(Text("Middle second"))))),
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Inner first Inner next line")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text("Middle third")))))
+ )
+ ),
+ Li(listOf(P(listOf(Text("Outer third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ P(listOf(Text("New paragraph")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Header and two paragraphs`() {
+ val kdoc = """
+ | # Header 1
+ | Following text
+ |
+ | New paragraph
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ H1(listOf(Text("Header 1"))),
+ P(listOf(Text("Following text"))),
+ P(listOf(Text("New paragraph")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Disabled //TODO: ATX_2 to ATX_6 and sometimes ATX_1 from jetbrains parser consumes white space. Need to handle it in their library
+ @Test
+ fun `All headers`() {
+ val kdoc = """
+ | # Header 1
+ | Text 1
+ | ## Header 2
+ | Text 2
+ | ### Header 3
+ | Text 3
+ | #### Header 4
+ | Text 4
+ | ##### Header 5
+ | Text 5
+ | ###### Header 6
+ | Text 6
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ H1(listOf(Text("Header 1"))),
+ P(listOf(Text("Text 1"))),
+ H2(listOf(Text("Header 2"))),
+ P(listOf(Text("Text 2"))),
+ H3(listOf(Text("Header 3"))),
+ P(listOf(Text("Text 3"))),
+ H4(listOf(Text("Header 4"))),
+ P(listOf(Text("Text 4"))),
+ H5(listOf(Text("Header 5"))),
+ P(listOf(Text("Text 5"))),
+ H6(listOf(Text("Header 6"))),
+ P(listOf(Text("Text 6")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Bold New Line Bold`() {
+ val kdoc = """
+ | **line 1**\
+ | **line 2**
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ B(listOf(Text("line 1"))),
+ Br,
+ B(listOf(Text("line 2")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Horizontal rule`() {
+ val kdoc = """
+ | ***
+ | text 1
+ | ___
+ | text 2
+ | ***
+ | text 3
+ | ___
+ | text 4
+ | ***
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ HorizontalRule,
+ P(listOf(Text("text 1"))),
+ HorizontalRule,
+ P(listOf(Text("text 2"))),
+ HorizontalRule,
+ P(listOf(Text("text 3"))),
+ HorizontalRule,
+ P(listOf(Text("text 4"))),
+ HorizontalRule
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Blockquote`() {
+ val kdoc = """
+ | > Blockquotes are very handy in email to emulate reply text.
+ | > This line is part of the same quote.
+ |
+ | Quote break.
+ |
+ | > Quote
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ BlockQuote(
+ listOf(
+ P(
+ listOf(
+ Text("Blockquotes are very handy in email to emulate reply text. This line is part of the same quote.")
+ )
+ )
+ )
+ ),
+ P(listOf(Text("Quote break."))),
+ BlockQuote(
+ listOf(
+ P(listOf(Text("Quote")))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+
+ @Test
+ fun `Blockquote nested`() {
+ val kdoc = """
+ | > text 1
+ | > text 2
+ | >> text 3
+ | >> text 4
+ | >
+ | > text 5
+ |
+ | Quote break.
+ |
+ | > Quote
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ BlockQuote(
+ listOf(
+ P(listOf(Text("text 1 text 2"))),
+ BlockQuote(
+ listOf(
+ P(listOf(Text("text 3 text 4")))
+ )
+ ),
+ P(listOf(Text("text 5")))
+ )
+ ),
+ P(listOf(Text("Quote break."))),
+ BlockQuote(
+ listOf(
+ P(listOf(Text("Quote")))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Disabled //TODO: Again ATX_1 consumes white space
+ @Test
+ fun `Blockquote nested with fancy text enhancement`() {
+ val kdoc = """
+ | > text **1**
+ | > text 2
+ | >> # text 3
+ | >> * text 4
+ | >> * text 5
+ | >
+ | > text 6
+ |
+ | Quote break.
+ |
+ | > Quote
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ BlockQuote(
+ listOf(
+ P(
+ listOf(
+ Text("text "),
+ B(listOf(Text("1"))),
+ Text("\ntext 2")
+ )
+ ),
+ BlockQuote(
+ listOf(
+ H1(listOf(Text("text 3"))),
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("text 4"))))),
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("text 5")))))
+ )
+ )
+ )
+ )
+ )
+ ),
+ P(listOf(Text("text 6")))
+ )
+ ),
+ P(listOf(Text("Quote break."))),
+ BlockQuote(
+ listOf(
+ P(listOf(Text("Quote")))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Simple Code Block`() {
+ val kdoc = """
+ | `Some code`
+ | Sample text
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ CodeInline(listOf(Text("Some code"))),
+ Text(" Sample text")
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Multilined Code Block`() {
+ val kdoc = """
+ | ```kotlin
+ | val x: Int = 0
+ | val y: String = "Text"
+ |
+ | val z: Boolean = true
+ | for(i in 0..10) {
+ | println(i)
+ | }
+ | ```
+ | Sample text
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ CodeBlock(
+ listOf(
+ Text("val x: Int = 0"), Br,
+ Text("val y: String = \"Text\""), Br, Br,
+ Text(" val z: Boolean = true"), Br,
+ Text("for(i in 0..10) {"), Br,
+ Text(" println(i)"), Br,
+ Text("}")
+ ),
+ mapOf("lang" to "kotlin")
+ ),
+ P(listOf(Text("Sample text")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+
+ @Test
+ fun `Inline link`() {
+ val kdoc = """
+ | [I'm an inline-style link](https://www.google.com)
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ A(
+ listOf(Text("I'm an inline-style link")),
+ mapOf("href" to "https://www.google.com")
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Inline link with title`() {
+ val kdoc = """
+ | [I'm an inline-style link with title](https://www.google.com "Google's Homepage")
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ A(
+ listOf(Text("I'm an inline-style link with title")),
+ mapOf("href" to "https://www.google.com", "title" to "Google's Homepage")
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Full reference link`() {
+ val kdoc = """
+ | [I'm a reference-style link][Arbitrary case-insensitive reference text]
+ |
+ | [arbitrary case-insensitive reference text]: https://www.mozilla.org
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ P(
+ listOf(
+ A(
+ listOf(Text("I'm a reference-style link")),
+ mapOf("href" to "https://www.mozilla.org")
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Full reference link with number`() {
+ val kdoc = """
+ | [You can use numbers for reference-style link definitions][1]
+ |
+ | [1]: http://slashdot.org
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ P(
+ listOf(
+ A(
+ listOf(Text("You can use numbers for reference-style link definitions")),
+ mapOf("href" to "http://slashdot.org")
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Short reference link`() {
+ val kdoc = """
+ | Or leave it empty and use the [link text itself].
+ |
+ | [link text itself]: http://www.reddit.com
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ P(
+ listOf(
+ Text("Or leave it empty and use the "),
+ A(
+ listOf(Text("link text itself")),
+ mapOf("href" to "http://www.reddit.com")
+ ),
+ Text(".")
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Autolink`() {
+ val kdoc = """
+ | URLs and URLs in angle brackets will automatically get turned into links.
+ | http://www.example.com or <http://www.example.com> and sometimes
+ | example.com (but not on Github, for example).
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Text("URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or "),
+ A(
+ listOf(Text("http://www.example.com")),
+ mapOf("href" to "http://www.example.com")
+ ),
+ Text(" and sometimes example.com (but not on Github, for example).")
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Various links`() {
+ val kdoc = """
+ | [I'm an inline-style link](https://www.google.com)
+ |
+ | [I'm an inline-style link with title](https://www.google.com "Google's Homepage")
+ |
+ | [I'm a reference-style link][Arbitrary case-insensitive reference text]
+ |
+ | [You can use numbers for reference-style link definitions][1]
+ |
+ | Or leave it empty and use the [link text itself].
+ |
+ | URLs and URLs in angle brackets will automatically get turned into links.
+ | http://www.example.com or <http://www.example.com> and sometimes
+ | example.com (but not on Github, for example).
+ |
+ | Some text to show that the reference links can follow later.
+ |
+ | [arbitrary case-insensitive reference text]: https://www.mozilla.org
+ | [1]: http://slashdot.org
+ | [link text itself]: http://www.reddit.com
+ """.trimMargin()
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ P(
+ listOf(
+ A(
+ listOf(Text("I'm an inline-style link")),
+ mapOf("href" to "https://www.google.com")
+ )
+ )
+ ),
+ P(
+ listOf(
+ A(
+ listOf(Text("I'm an inline-style link with title")),
+ mapOf("href" to "https://www.google.com", "title" to "Google's Homepage")
+ )
+ )
+ ),
+ P(
+ listOf(
+ A(
+ listOf(Text("I'm a reference-style link")),
+ mapOf("href" to "https://www.mozilla.org")
+ )
+ )
+ ),
+ P(
+ listOf(
+ A(
+ listOf(Text("You can use numbers for reference-style link definitions")),
+ mapOf("href" to "http://slashdot.org")
+ )
+ )
+ ),
+ P(
+ listOf(
+ Text("Or leave it empty and use the "),
+ A(
+ listOf(Text("link text itself")),
+ mapOf("href" to "http://www.reddit.com")
+ ),
+ Text(".")
+ )
+ ),
+ P(
+ listOf(
+ Text("URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or "),
+ A(
+ listOf(Text("http://www.example.com")),
+ mapOf("href" to "http://www.example.com")
+ ),
+ Text(" and sometimes example.com (but not on Github, for example).")
+ )
+ ),
+ P(listOf(Text("Some text to show that the reference links can follow later.")))
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+
+ @Test
+ fun `Windows Carriage Return Line Feed`() {
+ val kdoc = "text\r\ntext"
+ val expectedDocumentationNode = DocumentationNode(
+ listOf(
+ Description(
+ P(
+ listOf(
+ Text("text text")
+ )
+ )
+ )
+ )
+ )
+ executeTest(kdoc, expectedDocumentationNode)
+ }
+}
+
diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt
new file mode 100644
index 00000000..5dc8812e
--- /dev/null
+++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt
@@ -0,0 +1,533 @@
+package model
+
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.sureClassNames
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.KotlinModifier.*
+import org.junit.jupiter.api.Assertions.assertNull
+import org.junit.jupiter.api.Test
+import utils.AbstractModelTest
+import utils.assertNotNull
+import utils.name
+import utils.supers
+
+
+class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") {
+
+ @Test
+ fun emptyClass() {
+ inlineModelTest(
+ """
+ |class Klass {}"""
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ children counts 4
+ }
+ }
+ }
+
+ @Test
+ fun emptyObject() {
+ inlineModelTest(
+ """
+ |object Obj {}
+ """
+ ) {
+ with((this / "classes" / "Obj").cast<DObject>()) {
+ name equals "Obj"
+ children counts 3
+ }
+ }
+ }
+
+ @Test
+ fun classWithConstructor() {
+ inlineModelTest(
+ """
+ |class Klass(name: String)
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ children counts 4
+
+ with(constructors.firstOrNull().assertNotNull("Constructor")) {
+ visibility.values allEquals KotlinVisibility.Public
+ parameters counts 1
+ with(parameters.firstOrNull().assertNotNull("Constructor parameter")) {
+ name equals "name"
+ type.name equals "String"
+ }
+ }
+
+ }
+ }
+ }
+
+ @Test
+ fun classWithFunction() {
+ inlineModelTest(
+ """
+ |class Klass {
+ | fun fn() {}
+ |}
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ children counts 5
+
+ with((this / "fn").cast<DFunction>()) {
+ type.name equals "Unit"
+ parameters counts 0
+ visibility.values allEquals KotlinVisibility.Public
+ }
+ }
+ }
+ }
+
+ @Test
+ fun classWithProperty() {
+ inlineModelTest(
+ """
+ |class Klass {
+ | val name: String = ""
+ |}
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ children counts 5
+
+ with((this / "name").cast<DProperty>()) {
+ name equals "name"
+ // TODO property name
+ }
+ }
+ }
+ }
+
+ @Test
+ fun classWithCompanionObject() {
+ inlineModelTest(
+ """
+ |class Klass() {
+ | companion object {
+ | val x = 1
+ | fun foo() {}
+ | }
+ |}
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ children counts 5
+
+ with((this / "Companion").cast<DObject>()) {
+ name equals "Companion"
+ children counts 5
+
+ with((this / "x").cast<DProperty>()) {
+ name equals "x"
+ }
+
+ with((this / "foo").cast<DFunction>()) {
+ name equals "foo"
+ parameters counts 0
+ type.name equals "Unit"
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun dataClass() {
+ inlineModelTest(
+ """
+ |data class Klass() {}
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ visibility.values allEquals KotlinVisibility.Public
+ with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("Extras")) {
+ this counts 1
+ first() equals ExtraModifiers.KotlinOnlyModifiers.Data
+ }
+ }
+ }
+ }
+
+ @Test
+ fun sealedClass() {
+ inlineModelTest(
+ """
+ |sealed class Klass() {}
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ modifier.values.forEach { it equals Sealed }
+ }
+ }
+ }
+
+ @Test
+ fun annotatedClassWithAnnotationParameters() {
+ inlineModelTest(
+ """
+ |@Deprecated("should no longer be used") class Foo() {}
+ """
+ ) {
+ with((this / "classes" / "Foo").cast<DClass>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "Deprecated"
+ params.entries counts 1
+ (params["message"].assertNotNull("message") as StringValue).value equals "\"should no longer be used\""
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun notOpenClass() {
+ inlineModelTest(
+ """
+ |open class C() {
+ | open fun f() {}
+ |}
+ |
+ |class D() : C() {
+ | override fun f() {}
+ |}
+ """
+ ) {
+ val C = (this / "classes" / "C").cast<DClass>()
+ val D = (this / "classes" / "D").cast<DClass>()
+
+ with(C) {
+ modifier.values.forEach { it equals Open }
+ with((this / "f").cast<DFunction>()) {
+ modifier.values.forEach { it equals Open }
+ }
+ }
+ with(D) {
+ modifier.values.forEach { it equals Final }
+ with((this / "f").cast<DFunction>()) {
+ modifier.values.forEach { it equals Open }
+ }
+ D.supertypes.flatMap { it.component2() }.firstOrNull()?.dri equals C.dri
+ }
+ }
+ }
+
+ @Test
+ fun indirectOverride() {
+ inlineModelTest(
+ """
+ |abstract class C() {
+ | abstract fun foo()
+ |}
+ |
+ |abstract class D(): C()
+ |
+ |class E(): D() {
+ | override fun foo() {}
+ |}
+ """
+ ) {
+ val C = (this / "classes" / "C").cast<DClass>()
+ val D = (this / "classes" / "D").cast<DClass>()
+ val E = (this / "classes" / "E").cast<DClass>()
+
+ with(C) {
+ modifier.values.forEach { it equals Abstract }
+ ((this / "foo").cast<DFunction>()).modifier.values.forEach { it equals Abstract }
+ }
+
+ with(D) {
+ modifier.values.forEach { it equals Abstract }
+ }
+
+ with(E) {
+ modifier.values.forEach { it equals Final }
+
+ }
+ D.supers.single().dri equals C.dri
+ E.supers.single().dri equals D.dri
+ }
+ }
+
+ @Test
+ fun innerClass() {
+ inlineModelTest(
+ """
+ |class C {
+ | inner class D {}
+ |}
+ """
+ ) {
+ with((this / "classes" / "C").cast<DClass>()) {
+
+ with((this / "D").cast<DClass>()) {
+ with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("AdditionalModifiers")) {
+ this counts 1
+ first() equals ExtraModifiers.KotlinOnlyModifiers.Inner
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun companionObjectExtension() {
+ inlineModelTest(
+ """
+ |class Klass {
+ | companion object Default {}
+ |}
+ |
+ |/**
+ | * The def
+ | */
+ |val Klass.Default.x: Int get() = 1
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+
+ with((this / "Default").cast<DObject>()) {
+ name equals "Default"
+ // TODO extensions
+ }
+ }
+ }
+ }
+
+// @Test fun companionObjectExtension() {
+// checkSourceExistsAndVerifyModel("testdata/classes/companionObjectExtension.kt", defaultModelConfig) { model ->
+// val pkg = model.members.single()
+// val cls = pkg.members.single { it.name == "Foo" }
+// val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty }
+// assertEquals(1, extensions.size)
+// }
+// }
+
+ @Test
+ fun secondaryConstructor() {
+ inlineModelTest(
+ """
+ |class C() {
+ | /** This is a secondary constructor. */
+ | constructor(s: String): this() {}
+ |}
+ """
+ ) {
+ with((this / "classes" / "C").cast<DClass>()) {
+ name equals "C"
+ constructors counts 2
+
+ constructors.map { it.name } allEquals "<init>"
+
+ with(constructors.find { it.parameters.isNullOrEmpty() } notNull "C()") {
+ parameters counts 0
+ }
+
+ with(constructors.find { it.parameters.isNotEmpty() } notNull "C(String)") {
+ parameters counts 1
+ with(parameters.firstOrNull() notNull "Constructor parameter") {
+ name equals "s"
+ type.name equals "String"
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun sinceKotlin() {
+ inlineModelTest(
+ """
+ |/**
+ | * Useful
+ | */
+ |@SinceKotlin("1.1")
+ |class C
+ """
+ ) {
+ with((this / "classes" / "C").cast<DClass>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "SinceKotlin"
+ params.entries counts 1
+ (params["version"].assertNotNull("version") as StringValue).value equals "\"1.1\""
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun privateCompanionObject() {
+ inlineModelTest(
+ """
+ |class Klass {
+ | private companion object {
+ | fun fn() {}
+ | val a = 0
+ | }
+ |}
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ assertNull(companion, "Companion should not be visible by default")
+ }
+ }
+ }
+
+ @Test
+ fun companionObject() {
+ inlineModelTest(
+ """
+ |class Klass {
+ | companion object {
+ | fun fn() {}
+ | val a = 0
+ | }
+ |}
+ """
+ ) {
+ with((this / "classes" / "Klass").cast<DClass>()) {
+ name equals "Klass"
+ with((this / "Companion").cast<DObject>()) {
+ name equals "Companion"
+ visibility.values allEquals KotlinVisibility.Public
+
+ with((this / "fn").cast<DFunction>()) {
+ name equals "fn"
+ parameters counts 0
+ receiver equals null
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun annotatedClass() {
+ inlineModelTest(
+ """@Suppress("abc") class Foo() {}"""
+ ) {
+ with((this / "classes" / "Foo").cast<DClass>()) {
+ with(extra[Annotations]?.content?.values?.firstOrNull()?.firstOrNull().assertNotNull("annotations")) {
+ dri.toString() equals "kotlin/Suppress///PointingToDeclaration/"
+ (params["names"].assertNotNull("param") as ArrayValue).value equals listOf(StringValue("\"abc\""))
+ }
+ }
+ }
+ }
+
+ @Test
+ fun javaAnnotationClass() {
+ inlineModelTest(
+ """
+ |import java.lang.annotation.Retention
+ |import java.lang.annotation.RetentionPolicy
+ |
+ |@Retention(RetentionPolicy.SOURCE)
+ |public annotation class throws()
+ """
+ ) {
+ with((this / "classes" / "throws").cast<DAnnotation>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "Retention"
+ params["value"].assertNotNull("value") equals EnumValue(
+ "RetentionPolicy.SOURCE",
+ DRI("java.lang.annotation", "RetentionPolicy.SOURCE")
+ )
+ }
+ }
+ }
+ }
+ }
+
+ @Test fun genericAnnotationClass() {
+ inlineModelTest(
+ """annotation class Foo<A,B,C,D:Number>() {}"""
+ ) {
+ with((this / "classes" / "Foo").cast<DAnnotation>()){
+ generics.map { it.name to it.bounds.first().name } equals listOf("A" to "Any", "B" to "Any", "C" to "Any", "D" to "Number")
+ }
+ }
+ }
+
+ @Test fun nestedGenericClasses(){
+ inlineModelTest(
+ """
+ |class Outer<OUTER> {
+ | inner class Inner<INNER, T : OUTER> { }
+ |}
+ """.trimMargin()
+ ){
+ with((this / "classes" / "Outer").cast<DClass>()){
+ val inner = classlikes.single().cast<DClass>()
+ inner.generics.map { it.name to it.bounds.first().name } equals listOf("INNER" to "Any", "T" to "OUTER")
+ }
+ }
+ }
+
+ @Test fun allImplementedInterfaces() {
+ inlineModelTest(
+ """
+ | interface Highest { }
+ | open class HighestImpl: Highest { }
+ | interface Lower { }
+ | interface LowerImplInterface: Lower { }
+ | class Tested : HighestImpl(), LowerImplInterface { }
+ """.trimIndent()
+ ){
+ with((this / "classes" / "Tested").cast<DClass>()){
+ extra[ImplementedInterfaces]?.interfaces?.entries?.single()?.value?.map { it.sureClassNames }?.sorted() equals listOf("Highest", "Lower", "LowerImplInterface").sorted()
+ }
+ }
+ }
+
+ @Test fun multipleClassInheritance() {
+ inlineModelTest(
+ """
+ | open class A { }
+ | open class B: A() { }
+ | class Tested : B() { }
+ """.trimIndent()
+ ) {
+ with((this / "classes" / "Tested").cast<DClass>()) {
+ supertypes.entries.single().value.map { it.dri.sureClassNames }.single() equals "B"
+ }
+ }
+ }
+
+ @Test fun multipleClassInheritanceWithInterface(){
+ inlineModelTest(
+ """
+ | open class A { }
+ | open class B: A() { }
+ | interface X { }
+ | interface Y : X { }
+ | class Tested : B(), Y { }
+ """.trimIndent()
+ ){
+ with((this / "classes" / "Tested").cast<DClass>()) {
+ supertypes.entries.single().value.map { it.dri.sureClassNames to it.kind }.sortedBy { it.first } equals listOf("B" to KotlinClassKindTypes.CLASS, "Y" to KotlinClassKindTypes.INTERFACE)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/model/CommentTest.kt b/plugins/base/src/test/kotlin/model/CommentTest.kt
new file mode 100644
index 00000000..c1da8ee0
--- /dev/null
+++ b/plugins/base/src/test/kotlin/model/CommentTest.kt
@@ -0,0 +1,332 @@
+package model
+
+import org.jetbrains.dokka.model.DProperty
+import org.jetbrains.dokka.model.doc.CustomTagWrapper
+import org.jetbrains.dokka.model.doc.Text
+import org.junit.jupiter.api.Test
+import utils.*
+
+class CommentTest : AbstractModelTest("/src/main/kotlin/comment/Test.kt", "comment") {
+
+ @Test
+ fun codeBlockComment() {
+ inlineModelTest(
+ """
+ |/**
+ | * ```brainfuck
+ | * ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.
+ | * ```
+ | */
+ |val prop1 = ""
+ |
+ |
+ |/**
+ | * ```
+ | * a + b - c
+ | * ```
+ | */
+ |val prop2 = ""
+ """
+ ) {
+ with((this / "comment" / "prop1").cast<DProperty>()) {
+ name equals "prop1"
+ with(this.docs().firstOrNull()?.root.assertNotNull("Code")) {
+ (children.firstOrNull() as? Text)
+ ?.body equals "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>."
+
+ params["lang"] equals "brainfuck"
+ }
+ }
+ with((this / "comment" / "prop2").cast<DProperty>()) {
+ name equals "prop2"
+ comments() equals "a + b - c"
+ }
+ }
+ }
+
+ @Test
+ fun emptyDoc() {
+ inlineModelTest(
+ """
+ val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ name equals "property"
+ comments() equals ""
+ }
+ }
+ }
+
+ @Test
+ fun emptyDocButComment() {
+ inlineModelTest(
+ """
+ |/* comment */
+ |val property = "test"
+ |fun tst() = property
+ """
+ ) {
+ val p = this
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals ""
+ }
+ }
+ }
+
+ @Test
+ fun multilineDoc() {
+ inlineModelTest(
+ """
+ |/**
+ | * doc1
+ | *
+ | * doc2
+ | * doc3
+ | */
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "doc1\ndoc2 doc3"
+ }
+ }
+ }
+
+ @Test
+ fun multilineDocWithComment() {
+ inlineModelTest(
+ """
+ |/**
+ | * doc1
+ | *
+ | * doc2
+ | * doc3
+ | */
+ |// comment
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "doc1\ndoc2 doc3"
+ }
+ }
+ }
+
+ @Test
+ fun oneLineDoc() {
+ inlineModelTest(
+ """
+ |/** doc */
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "doc"
+ }
+ }
+ }
+
+ @Test
+ fun oneLineDocWithComment() {
+ inlineModelTest(
+ """
+ |/** doc */
+ |// comment
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "doc"
+ }
+ }
+ }
+
+ @Test
+ fun oneLineDocWithEmptyLine() {
+ inlineModelTest(
+ """
+ |/** doc */
+ |
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "doc"
+ }
+ }
+ }
+
+ @Test
+ fun emptySection() {
+ inlineModelTest(
+ """
+ |/**
+ | * Summary
+ | * @one
+ | */
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "Summary\none: []"
+ docs().find { it is CustomTagWrapper && it.name == "one" }.let {
+ with(it.assertNotNull("'one' entry")) {
+ root.children counts 0
+ root.params.keys counts 0
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun quotes() {
+ inlineModelTest(
+ """
+ |/** it's "useful" */
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals """it's "useful""""
+ }
+ }
+ }
+
+ @Test
+ fun section1() {
+ inlineModelTest(
+ """
+ |/**
+ | * Summary
+ | * @one section one
+ | */
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "Summary\none: [section one]"
+ }
+ }
+ }
+
+
+ @Test
+ fun section2() {
+ inlineModelTest(
+ """
+ |/**
+ | * Summary
+ | * @one section one
+ | * @two section two
+ | */
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "Summary\none: [section one]\ntwo: [section two]"
+ }
+ }
+ }
+
+ @Test
+ fun multilineSection() {
+ inlineModelTest(
+ """
+ |/**
+ | * Summary
+ | * @one
+ | * line one
+ | * line two
+ | */
+ |val property = "test"
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ comments() equals "Summary\none: [line one line two]"
+ }
+ }
+ }
+
+// @Test todo
+ fun directive() {
+ inlineModelTest(
+ """
+ |/**
+ | * Summary
+ | *
+ | * @sample example1
+ | * @sample example2
+ | * @sample X.example3
+ | * @sample X.Y.example4
+ | */
+ |val property = "test"
+ |
+ |fun example1(node: String) = if (true) {
+ | println(property)
+ |}
+ |
+ |fun example2(node: String) {
+ | if (true) {
+ | println(property)
+ | }
+ |}
+ |
+ |class X {
+ | fun example3(node: String) {
+ | if (true) {
+ | println(property)
+ | }
+ | }
+ |
+ | class Y {
+ | fun example4(node: String) {
+ | if (true) {
+ | println(property)
+ | }
+ | }
+ | }
+ |}
+ """
+ ) {
+ with((this / "comment" / "property").cast<DProperty>()) {
+ this
+ }
+ }
+ }
+
+
+// @Test fun directive() {
+// checkSourceExistsAndVerifyModel("testdata/comments/directive.kt", defaultModelConfig) { model ->
+// with(model.members.single().members.first()) {
+// assertEquals("Summary", content.summary.toTestString())
+// with (content.description) {
+// assertEqualsIgnoringSeparators("""
+// |[code lang=kotlin]
+// |if (true) {
+// | println(property)
+// |}
+// |[/code]
+// |[code lang=kotlin]
+// |if (true) {
+// | println(property)
+// |}
+// |[/code]
+// |[code lang=kotlin]
+// |if (true) {
+// | println(property)
+// |}
+// |[/code]
+// |[code lang=kotlin]
+// |if (true) {
+// | println(property)
+// |}
+// |[/code]
+// |""".trimMargin(), toTestString())
+// }
+// }
+// }
+// }
+
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/model/FunctionsTest.kt b/plugins/base/src/test/kotlin/model/FunctionsTest.kt
new file mode 100644
index 00000000..c96e7df6
--- /dev/null
+++ b/plugins/base/src/test/kotlin/model/FunctionsTest.kt
@@ -0,0 +1,396 @@
+package model
+
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
+import org.junit.jupiter.api.Test
+import utils.AbstractModelTest
+import utils.assertNotNull
+import utils.comments
+import utils.name
+
+class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "function") {
+
+ @Test
+ fun function() {
+ inlineModelTest(
+ """
+ |/**
+ | * Function fn
+ | */
+ |fun fn() {}
+ """
+ ) {
+ with((this / "function" / "fn").cast<DFunction>()) {
+ name equals "fn"
+ type.name equals "Unit"
+ this.children.assertCount(0, "Function children: ")
+ }
+ }
+ }
+
+ @Test
+ fun overloads() {
+ inlineModelTest(
+ """
+ |/**
+ | * Function fn
+ | */
+ |fun fn() {}
+ | /**
+ | * Function fn(Int)
+ | */
+ |fun fn(i: Int) {}
+ """
+ ) {
+ with((this / "function").cast<DPackage>()) {
+ val fn1 = functions.find {
+ it.name == "fn" && it.parameters.isNullOrEmpty()
+ }.assertNotNull("fn()")
+ val fn2 = functions.find {
+ it.name == "fn" && it.parameters.isNotEmpty()
+ }.assertNotNull("fn(Int)")
+
+ with(fn1) {
+ name equals "fn"
+ parameters.assertCount(0)
+ }
+
+ with(fn2) {
+ name equals "fn"
+ parameters.assertCount(1)
+ parameters.first().type.name equals "Int"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun functionWithReceiver() {
+ inlineModelTest(
+ """
+ |/**
+ | * Function with receiver
+ | */
+ |fun String.fn() {}
+ |
+ |/**
+ | * Function with receiver
+ | */
+ |fun String.fn(x: Int) {}
+ """
+ ) {
+ with((this / "function").cast<DPackage>()) {
+ val fn1 = functions.find {
+ it.name == "fn" && it.parameters.isNullOrEmpty()
+ }.assertNotNull("fn()")
+ val fn2 = functions.find {
+ it.name == "fn" && it.parameters.count() == 1
+ }.assertNotNull("fn(Int)")
+
+ with(fn1) {
+ name equals "fn"
+ parameters counts 0
+ receiver.assertNotNull("fn() receiver")
+ }
+
+ with(fn2) {
+ name equals "fn"
+ parameters counts 1
+ receiver.assertNotNull("fn(Int) receiver")
+ parameters.first().type.name equals "Int"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun functionWithParams() {
+ inlineModelTest(
+ """
+ |/**
+ | * Multiline
+ | *
+ | * Function
+ | * Documentation
+ | */
+ |fun function(/** parameter */ x: Int) {
+ |}
+ """
+ ) {
+ with((this / "function" / "function").cast<DFunction>()) {
+ comments() equals "Multiline\nFunction Documentation"
+
+ name equals "function"
+ parameters counts 1
+ parameters.firstOrNull().assertNotNull("Parameter: ").also {
+ it.name equals "x"
+ it.type.name equals "Int"
+ it.comments() equals "parameter"
+ }
+
+ type.assertNotNull("Return type: ").name equals "Unit"
+ }
+ }
+ }
+
+ @Test
+ fun functionWithNotDocumentedAnnotation() {
+ inlineModelTest(
+ """
+ |@Suppress("FOO") fun f() {}
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "Suppress"
+ params.entries counts 1
+ (params["names"].assertNotNull("param") as ArrayValue).value equals listOf(StringValue("\"FOO\""))
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun inlineFunction() {
+ inlineModelTest(
+ """
+ |inline fun f(a: () -> String) {}
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ extra[AdditionalModifiers]!!.content.entries.single().value counts 1
+ extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Inline
+ }
+ }
+ }
+
+ @Test
+ fun suspendFunction() {
+ inlineModelTest(
+ """
+ |suspend fun f() {}
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ extra[AdditionalModifiers]!!.content.entries.single().value counts 1
+ extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Suspend
+ }
+ }
+ }
+
+ @Test
+ fun suspendInlineFunctionOrder() {
+ inlineModelTest(
+ """
+ |suspend inline fun f(a: () -> String) {}
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ extra[AdditionalModifiers]!!.content.entries.single().value counts 2
+ extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Suspend
+ extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Inline
+ }
+ }
+ }
+
+ @Test
+ fun inlineSuspendFunctionOrderChanged() {
+ inlineModelTest(
+ """
+ |inline suspend fun f(a: () -> String) {}
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("AdditionalModifiers")) {
+ this counts 2
+ this exists ExtraModifiers.KotlinOnlyModifiers.Suspend
+ this exists ExtraModifiers.KotlinOnlyModifiers.Inline
+ }
+ }
+ }
+ }
+
+ @Test
+ fun functionWithAnnotatedParam() {
+ inlineModelTest(
+ """
+ |@Target(AnnotationTarget.VALUE_PARAMETER)
+ |@Retention(AnnotationRetention.SOURCE)
+ |@MustBeDocumented
+ |public annotation class Fancy
+ |
+ |fun function(@Fancy notInlined: () -> Unit) {}
+ """
+ ) {
+ with((this / "function" / "Fancy").cast<DAnnotation>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 3
+ with(map { it.dri.classNames to it }.toMap()) {
+ with(this["Target"].assertNotNull("Target")) {
+ (params["allowedTargets"].assertNotNull("allowedTargets") as ArrayValue).value equals listOf(
+ EnumValue(
+ "AnnotationTarget.VALUE_PARAMETER",
+ DRI("kotlin.annotation", "AnnotationTarget.VALUE_PARAMETER")
+ )
+ )
+ }
+ with(this["Retention"].assertNotNull("Retention")) {
+ (params["value"].assertNotNull("value") as EnumValue) equals EnumValue(
+ "AnnotationRetention.SOURCE",
+ DRI("kotlin.annotation", "AnnotationRetention.SOURCE")
+ )
+ }
+ this["MustBeDocumented"].assertNotNull("MustBeDocumented").params.entries counts 0
+ }
+ }
+
+ }
+ with((this / "function" / "function" / "notInlined").cast<DParameter>()) {
+ with(this.extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "Fancy"
+ params.entries counts 0
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun functionWithNoinlineParam() {
+ inlineModelTest(
+ """
+ |fun f(noinline notInlined: () -> Unit) {}
+ """
+ ) {
+ with((this / "function" / "f" / "notInlined").cast<DParameter>()) {
+ extra[AdditionalModifiers]!!.content.entries.single().value counts 1
+ extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.NoInline
+ }
+ }
+ }
+
+ @Test
+ fun annotatedFunctionWithAnnotationParameters() {
+ inlineModelTest(
+ """
+ |@Target(AnnotationTarget.VALUE_PARAMETER)
+ |@Retention(AnnotationRetention.SOURCE)
+ |@MustBeDocumented
+ |public annotation class Fancy(val size: Int)
+ |
+ |@Fancy(1) fun f() {}
+ """
+ ) {
+ with((this / "function" / "Fancy").cast<DAnnotation>()) {
+ constructors counts 1
+ with(constructors.first()) {
+ parameters counts 1
+ with(parameters.first()) {
+ type.name equals "Int"
+ name equals "size"
+ }
+ }
+
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 3
+ with(map { it.dri.classNames to it }.toMap()) {
+ with(this["Target"].assertNotNull("Target")) {
+ (params["allowedTargets"].assertNotNull("allowedTargets") as ArrayValue).value equals listOf(
+ EnumValue(
+ "AnnotationTarget.VALUE_PARAMETER",
+ DRI("kotlin.annotation", "AnnotationTarget.VALUE_PARAMETER")
+ )
+ )
+ }
+ with(this["Retention"].assertNotNull("Retention")) {
+ (params["value"].assertNotNull("value") as EnumValue) equals EnumValue(
+ "AnnotationRetention.SOURCE",
+ DRI("kotlin.annotation", "AnnotationRetention.SOURCE")
+ )
+ }
+ this["MustBeDocumented"].assertNotNull("MustBeDocumented").params.entries counts 0
+ }
+ }
+
+ }
+ with((this / "function" / "f").cast<DFunction>()) {
+ with(this.extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(this.first()) {
+ dri.classNames equals "Fancy"
+ params.entries counts 1
+ (params["size"] as StringValue).value equals "1"
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun functionWithDefaultStringParameter() {
+ inlineModelTest(
+ """
+ |/src/main/kotlin/function/Test.kt
+ |package function
+ |fun f(x: String = "") {}
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ parameters.forEach { p ->
+ p.name equals "x"
+ p.type.name.assertNotNull("Parameter type: ") equals "String"
+ p.extra[DefaultValue]?.value equals "\"\""
+ }
+ }
+ }
+ }
+
+ @Test
+ fun functionWithDefaultFloatParameter() {
+ inlineModelTest(
+ """
+ |/src/main/kotlin/function/Test.kt
+ |package function
+ |fun f(x: Float = 3.14f) {}
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ parameters.forEach { p ->
+ p.name equals "x"
+ p.type.name.assertNotNull("Parameter type: ") equals "Float"
+ p.extra[DefaultValue]?.value equals "3.14f"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun sinceKotlin() {
+ inlineModelTest(
+ """
+ |/**
+ | * Quite useful [String]
+ | */
+ |@SinceKotlin("1.1")
+ |fun f(): String = "1.1 rulezz"
+ """
+ ) {
+ with((this / "function" / "f").cast<DFunction>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "SinceKotlin"
+ params.entries counts 1
+ (params["version"].assertNotNull("version") as StringValue).value equals "\"1.1\""
+ }
+ }
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/model/InheritorsTest.kt b/plugins/base/src/test/kotlin/model/InheritorsTest.kt
new file mode 100644
index 00000000..503cf50c
--- /dev/null
+++ b/plugins/base/src/test/kotlin/model/InheritorsTest.kt
@@ -0,0 +1,95 @@
+package model
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.base.transformers.documentables.InheritorsExtractorTransformer
+import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo
+import org.jetbrains.dokka.model.DInterface
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+import utils.AbstractModelTest
+import utils.assertNotNull
+
+class InheritorsTest : AbstractModelTest("/src/main/kotlin/inheritors/Test.kt", "inheritors") {
+
+ object InheritorsPlugin : DokkaPlugin() {
+ val inheritors by extending {
+ CoreExtensions.documentableTransformer with InheritorsExtractorTransformer()
+ }
+ }
+
+ @Disabled("reenable after fixing subtypes")
+ @Test
+ fun simple() {
+ inlineModelTest(
+ """|interface A{}
+ |class B() : A {}
+ """.trimMargin(),
+ pluginsOverrides = listOf(InheritorsPlugin)
+ ) {
+ with((this / "inheritors" / "A").cast<DInterface>()) {
+ val map = extra[InheritorsInfo].assertNotNull("InheritorsInfo").value
+ with(map.keys.also { it counts 1 }.find { it.analysisPlatform == Platform.jvm }.assertNotNull("jvm key").let { map[it]!! }
+ ) {
+ this counts 1
+ first().classNames equals "B"
+ }
+ }
+ }
+ }
+
+ @Disabled("reenable after fixing subtypes")
+ @Test
+ fun multiplatform() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("common/src/", "jvm/src/")
+ analysisPlatform = "jvm"
+ }
+ sourceSet {
+ sourceRoots = listOf("common/src/", "js/src/")
+ analysisPlatform = "js"
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/common/src/main/kotlin/inheritors/Test.kt
+ |package inheritors
+ |interface A{}
+ |/jvm/src/main/kotlin/inheritors/Test.kt
+ |package inheritors
+ |class B() : A {}
+ |/js/src/main/kotlin/inheritors/Test.kt
+ |package inheritors
+ |class B() : A {}
+ |class C() : A {}
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = false,
+ pluginOverrides = listOf(InheritorsPlugin)
+ ) {
+ documentablesTransformationStage = { m ->
+ with((m / "inheritors" / "A").cast<DInterface>()) {
+ val map = extra[InheritorsInfo].assertNotNull("InheritorsInfo").value
+ with(map.keys.also { it counts 2 }) {
+ with(find { it.analysisPlatform == Platform.jvm }.assertNotNull("jvm key").let { map[it]!! }) {
+ this counts 1
+ first().classNames equals "B"
+ }
+ with(find { it.analysisPlatform == Platform.js }.assertNotNull("js key").let { map[it]!! }) {
+ this counts 2
+ val classes = listOf("B", "C")
+ assertTrue(all { classes.contains(it.classNames) }, "One of subclasses missing in js" )
+ }
+ }
+
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt
new file mode 100644
index 00000000..1f042304
--- /dev/null
+++ b/plugins/base/src/test/kotlin/model/JavaTest.kt
@@ -0,0 +1,346 @@
+package model
+
+import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.sureClassNames
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.doc.Param
+import org.jetbrains.dokka.model.doc.Text
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+import utils.AbstractModelTest
+import utils.assertNotNull
+import utils.name
+
+class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") {
+
+ @Test
+ fun function() {
+ inlineModelTest(
+ """
+ |class Test {
+ | /**
+ | * Summary for Function
+ | * @param name is String parameter
+ | * @param value is int parameter
+ | */
+ | public void fn(String name, int value) {}
+ |}
+ """
+ ) {
+ with((this / "java" / "Test").cast<DClass>()) {
+ name equals "Test"
+ children counts 1
+ with((this / "fn").cast<DFunction>()) {
+ name equals "fn"
+ val params = parameters.map { it.documentation.values.first().children.first() as Param }
+ params.mapNotNull { it.firstChildOfTypeOrNull<Text>()?.body } equals listOf("is String parameter", "is int parameter")
+ }
+ }
+ }
+ }
+
+ @Test fun allImplementedInterfacesInJava() {
+ inlineModelTest(
+ """
+ |interface Highest { }
+ |interface Lower extends Highest { }
+ |class Extendable { }
+ |class Tested extends Extendable implements Lower { }
+ """){
+ with((this / "java" / "Tested").cast<DClass>()){
+ extra[ImplementedInterfaces]?.interfaces?.entries?.single()?.value?.map { it.sureClassNames }?.sorted() equals listOf("Highest", "Lower").sorted()
+ }
+ }
+ }
+
+ @Test fun multipleClassInheritanceWithInterface() {
+ inlineModelTest(
+ """
+ |interface Highest { }
+ |interface Lower extends Highest { }
+ |class Extendable { }
+ |class Tested extends Extendable implements Lower { }
+ """){
+ with((this / "java" / "Tested").cast<DClass>()) {
+ supertypes.entries.single().value.map { it.dri.sureClassNames to it.kind }.sortedBy { it.first } equals listOf("Extendable" to JavaClassKindTypes.CLASS, "Lower" to JavaClassKindTypes.INTERFACE)
+ }
+ }
+ }
+
+ @Test // todo
+ fun memberWithModifiers() {
+ inlineModelTest(
+ """
+ |class Test {
+ | /**
+ | * Summary for Function
+ | * @param name is String parameter
+ | * @param value is int parameter
+ | */
+ | public void fn(String name, int value) {}
+ |}
+ """
+ ) {
+ with((this / "java" / "Test" / "fn").cast<DFunction>()) {
+ this
+ }
+ }
+ }
+
+ @Test
+ fun superClass() {
+ inlineModelTest(
+ """
+ |public class Foo extends Exception implements Cloneable {}
+ """
+ ) {
+ with((this / "java" / "Foo").cast<DClass>()) {
+ val sups = listOf("Exception", "Cloneable")
+ assertTrue(
+ sups.all { s -> supertypes.values.flatten().any { it.dri.classNames == s } })
+ "Foo must extend ${sups.joinToString(", ")}"
+ }
+ }
+ }
+
+ @Test
+ fun arrayType() {
+ inlineModelTest(
+ """
+ |class Test {
+ | public String[] arrayToString(int[] data) {
+ | return null;
+ | }
+ |}
+ """
+ ) {
+ with((this / "java" / "Test").cast<DClass>()) {
+ name equals "Test"
+ children counts 1
+
+ with((this / "arrayToString").cast<DFunction>()) {
+ name equals "arrayToString"
+ type.name equals "Array"
+ with(parameters.firstOrNull().assertNotNull("parameters")) {
+ name equals "data"
+ type.name equals "Array"
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun typeParameter() {
+ inlineModelTest(
+ """
+ |class Foo<T extends Comparable<T>> {
+ | public <E> E foo();
+ |}
+ """
+ ) {
+ with((this / "java" / "Foo").cast<DClass>()) {
+ generics counts 1
+ }
+ }
+ }
+
+ @Test
+ fun constructors() {
+ inlineModelTest(
+ """
+ |class Test {
+ | public Test() {}
+ |
+ | public Test(String s) {}
+ |}
+ """
+ ) {
+ with((this / "java" / "Test").cast<DClass>()) {
+ name equals "Test"
+
+ constructors counts 2
+ constructors.find { it.parameters.isNullOrEmpty() }.assertNotNull("Test()")
+
+ with(constructors.find { it.parameters.isNotEmpty() }.assertNotNull("Test(String)")) {
+ parameters.firstOrNull()?.type?.name equals "String"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun innerClass() {
+ inlineModelTest(
+ """
+ |class InnerClass {
+ | public class D {}
+ |}
+ """
+ ) {
+ with((this / "java" / "InnerClass").cast<DClass>()) {
+ children counts 1
+ with((this / "D").cast<DClass>()) {
+ name equals "D"
+ children counts 0
+ }
+ }
+ }
+ }
+
+ @Test
+ fun varargs() {
+ inlineModelTest(
+ """
+ |class Foo {
+ | public void bar(String... x);
+ |}
+ """
+ ) {
+ with((this / "java" / "Foo").cast<DClass>()) {
+ name equals "Foo"
+ children counts 1
+
+ with((this / "bar").cast<DFunction>()) {
+ name equals "bar"
+ with(parameters.firstOrNull().assertNotNull("parameter")) {
+ name equals "x"
+ type.name equals "Array"
+ }
+ }
+ }
+ }
+ }
+
+ @Test // todo
+ fun fields() {
+ inlineModelTest(
+ """
+ |class Test {
+ | public int i;
+ | public static final String s;
+ |}
+ """
+ ) {
+ with((this / "java" / "Test").cast<DClass>()) {
+ children counts 2
+
+ with((this / "i").cast<DProperty>()) {
+ getter equals null
+ setter equals null
+ }
+
+ with((this / "s").cast<DProperty>()) {
+ getter equals null
+ setter equals null
+ }
+ }
+ }
+ }
+
+ @Test
+ fun staticMethod() {
+ inlineModelTest(
+ """
+ |class C {
+ | public static void foo() {}
+ |}
+ """
+ ) {
+ with((this / "java" / "C" / "foo").cast<DFunction>()) {
+ with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("AdditionalModifiers")) {
+ this counts 1
+ first() equals ExtraModifiers.JavaOnlyModifiers.Static
+ }
+ }
+ }
+ }
+
+ @Test
+ fun annotatedAnnotation() {
+ inlineModelTest(
+ """
+ |import java.lang.annotation.*;
+ |
+ |@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
+ |public @interface Attribute {
+ | String value() default "";
+ |}
+ """
+ ) {
+ with((this / "java" / "Attribute").cast<DAnnotation>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ with(single()) {
+ dri.classNames equals "Target"
+ (params["value"].assertNotNull("value") as ArrayValue).value equals listOf(
+ EnumValue("ElementType.FIELD", DRI("java.lang.annotation", "ElementType")),
+ EnumValue("ElementType.TYPE", DRI("java.lang.annotation", "ElementType")),
+ EnumValue("ElementType.METHOD", DRI("java.lang.annotation", "ElementType"))
+ )
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun javaLangObject() {
+ inlineModelTest(
+ """
+ |class Test {
+ | public Object fn() { return null; }
+ |}
+ """
+ ) {
+ with((this / "java" / "Test" / "fn").cast<DFunction>()) {
+ assertTrue(type is JavaObject)
+ }
+ }
+ }
+
+ @Test
+ fun enumValues() {
+ inlineModelTest(
+ """
+ |enum E {
+ | Foo
+ |}
+ """
+ ) {
+ with((this / "java" / "E").cast<DEnum>()) {
+ name equals "E"
+ entries counts 1
+
+ with((this / "Foo").cast<DEnumEntry>()) {
+ name equals "Foo"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun inheritorLinks() {
+ inlineModelTest(
+ """
+ |public class InheritorLinks {
+ | public static class Foo {}
+ |
+ | public static class Bar extends Foo {}
+ |}
+ """
+ ) {
+ with((this / "java" / "InheritorLinks").cast<DClass>()) {
+ val dri = (this / "Bar").assertNotNull("Foo dri").dri
+ with((this / "Foo").cast<DClass>()) {
+ with(extra[InheritorsInfo].assertNotNull("InheritorsInfo")) {
+ with(value.values.flatten().distinct()) {
+ this counts 1
+ first() equals dri
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/model/PackagesTest.kt b/plugins/base/src/test/kotlin/model/PackagesTest.kt
new file mode 100644
index 00000000..86222d95
--- /dev/null
+++ b/plugins/base/src/test/kotlin/model/PackagesTest.kt
@@ -0,0 +1,134 @@
+package model
+
+import org.jetbrains.dokka.model.DPackage
+import org.junit.jupiter.api.Test
+import utils.AbstractModelTest
+
+class PackagesTest : AbstractModelTest("/src/main/kotlin/packages/Test.kt", "packages") {
+
+ @Test
+ fun rootPackage() {
+ inlineModelTest(
+ """
+ |
+ """.trimIndent(),
+ prependPackage = false,
+ configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ displayName = "JVM"
+ }
+ }
+ }
+ ) {
+ with((this / "[JVM root]").cast<DPackage>()) {
+ name equals "[JVM root]"
+ children counts 0
+ }
+ }
+ }
+
+ @Test
+ fun simpleNamePackage() {
+ inlineModelTest(
+ """
+ |package simple
+ """.trimIndent(),
+ prependPackage = false
+ ) {
+ with((this / "simple").cast<DPackage>()) {
+ name equals "simple"
+ children counts 0
+ }
+ }
+ }
+
+ @Test
+ fun dottedNamePackage() {
+ inlineModelTest(
+ """
+ |package dot.name
+ """.trimIndent(),
+ prependPackage = false
+ ) {
+ with((this / "dot.name").cast<DPackage>()) {
+ name equals "dot.name"
+ children counts 0
+ }
+ }
+
+ }
+
+ @Test
+ fun multipleFiles() {
+ inlineModelTest(
+ """
+ |package dot.name
+ |/src/main/kotlin/packages/Test2.kt
+ |package simple
+ """.trimIndent(),
+ prependPackage = false
+ ) {
+ children counts 2
+ with((this / "dot.name").cast<DPackage>()) {
+ name equals "dot.name"
+ children counts 0
+ }
+ with((this / "simple").cast<DPackage>()) {
+ name equals "simple"
+ children counts 0
+ }
+ }
+ }
+
+ @Test
+ fun multipleFilesSamePackage() {
+ inlineModelTest(
+ """
+ |package simple
+ |/src/main/kotlin/packages/Test2.kt
+ |package simple
+ """.trimIndent(),
+ prependPackage = false
+ ) {
+ children counts 1
+ with((this / "simple").cast<DPackage>()) {
+ name equals "simple"
+ children counts 0
+ }
+ }
+ }
+
+ @Test
+ fun classAtPackageLevel() {
+ inlineModelTest(
+ """
+ |package simple.name
+ |
+ |class Foo {}
+ """.trimIndent(),
+ prependPackage = false
+ ) {
+ with((this / "simple.name").cast<DPackage>()) {
+ name equals "simple.name"
+ children counts 1
+ }
+ }
+ }
+
+ // todo
+// @Test fun suppressAtPackageLevel() {
+// verifyModel(
+// ModelConfig(
+// roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)),
+// perPackageOptions = listOf(
+// PackageOptionsImpl(prefix = "simple.name", suppress = true)
+// ),
+// analysisPlatform = analysisPlatform
+// )
+// ) { model ->
+// assertEquals(0, model.members.count())
+// }
+// }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/model/PropertyTest.kt b/plugins/base/src/test/kotlin/model/PropertyTest.kt
new file mode 100644
index 00000000..af952b43
--- /dev/null
+++ b/plugins/base/src/test/kotlin/model/PropertyTest.kt
@@ -0,0 +1,265 @@
+package model
+
+import org.jetbrains.dokka.model.*
+import org.junit.jupiter.api.Test
+import utils.AbstractModelTest
+import utils.assertNotNull
+import utils.name
+
+class PropertyTest : AbstractModelTest("/src/main/kotlin/property/Test.kt", "property") {
+
+ @Test
+ fun valueProperty() {
+ inlineModelTest(
+ """
+ |val property = "test""""
+ ) {
+ with((this / "property" / "property").cast<DProperty>()) {
+ name equals "property"
+ children counts 0
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "String"
+ }
+ type.name equals "String"
+ }
+ }
+ }
+
+ @Test
+ fun variableProperty() {
+ inlineModelTest(
+ """
+ |var property = "test"
+ """
+ ) {
+ with((this / "property" / "property").cast<DProperty>()) {
+ name equals "property"
+ children counts 0
+ setter.assertNotNull("Setter")
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "String"
+ }
+ type.name equals "String"
+ }
+ }
+ }
+
+ @Test
+ fun valuePropertyWithGetter() {
+ inlineModelTest(
+ """
+ |val property: String
+ | get() = "test"
+ """
+ ) {
+ with((this / "property" / "property").cast<DProperty>()) {
+ name equals "property"
+ children counts 0
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "String"
+ }
+ type.name equals "String"
+ }
+ }
+ }
+
+ @Test
+ fun variablePropertyWithAccessors() {
+ inlineModelTest(
+ """
+ |var property: String
+ | get() = "test"
+ | set(value) {}
+ """
+ ) {
+ with((this / "property" / "property").cast<DProperty>()) {
+ name equals "property"
+ children counts 0
+ setter.assertNotNull("Setter")
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "String"
+ }
+ visibility.values allEquals KotlinVisibility.Public
+ }
+ }
+ }
+
+ @Test
+ fun propertyWithReceiver() {
+ inlineModelTest(
+ """
+ |val String.property: Int
+ | get() = size() * 2
+ """
+ ) {
+ with((this / "property" / "property").cast<DProperty>()) {
+ name equals "property"
+ children counts 0
+ with(receiver.assertNotNull("property receiver")) {
+ name equals null
+ type.name equals "String"
+ }
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "Int"
+ }
+ visibility.values allEquals KotlinVisibility.Public
+ }
+ }
+ }
+
+ @Test
+ fun propertyOverride() {
+ inlineModelTest(
+ """
+ |open class Foo() {
+ | open val property: Int get() = 0
+ |}
+ |class Bar(): Foo() {
+ | override val property: Int get() = 1
+ |}
+ """
+ ) {
+ with((this / "property").cast<DPackage>()) {
+ with((this / "Foo" / "property").cast<DProperty>()) {
+ name equals "property"
+ children counts 0
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "Int"
+ }
+ }
+ with((this / "Bar" / "property").cast<DProperty>()) {
+ name equals "property"
+ children counts 0
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "Int"
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun sinceKotlin() {
+ inlineModelTest(
+ """
+ |/**
+ | * Quite useful [String]
+ | */
+ |@SinceKotlin("1.1")
+ |val prop: String = "1.1 rulezz"
+ """
+ ) {
+ with((this / "property" / "prop").cast<DProperty>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "SinceKotlin"
+ params.entries counts 1
+ (params["version"].assertNotNull("version") as StringValue).value equals "\"1.1\""
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun annotatedProperty() {
+ inlineModelTest(
+ """
+ |@Strictfp var property = "test"
+ """,
+ configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+ ) {
+ with((this / "property" / "property").cast<DProperty>()) {
+ with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) {
+ this counts 1
+ with(first()) {
+ dri.classNames equals "Strictfp"
+ params.entries counts 0
+ }
+ }
+ }
+ }
+ }
+
+ @Test fun genericTopLevelExtensionProperty(){
+ inlineModelTest(
+ """ | val <T : Number> List<T>.sampleProperty: T
+ | get() { TODO() }
+ """.trimIndent()
+ ){
+ with((this / "property" / "sampleProperty").cast<DProperty>()) {
+ name equals "sampleProperty"
+ with(receiver.assertNotNull("Property receiver")) {
+ type.name equals "List"
+ }
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "T"
+ }
+ setter equals null
+ generics counts 1
+ generics.forEach {
+ it.name equals "T"
+ it.bounds.first().name equals "Number"
+ }
+ visibility.values allEquals KotlinVisibility.Public
+ }
+ }
+ }
+
+ @Test fun genericExtensionPropertyInClass(){
+ inlineModelTest(
+ """ | package test
+ | class XD<T> {
+ | var List<T>.sampleProperty: T
+ | get() { TODO() }
+ | set(value) { TODO() }
+ | }
+ """.trimIndent()
+ ){
+ with((this / "property" / "XD" / "sampleProperty").cast<DProperty>()) {
+ name equals "sampleProperty"
+ children counts 0
+ with(receiver.assertNotNull("Property receiver")) {
+ type.name equals "List"
+ }
+ with(getter.assertNotNull("Getter")) {
+ type.name equals "T"
+ }
+ with(setter.assertNotNull("Setter")){
+ type.name equals "Unit"
+ }
+ generics counts 0
+ visibility.values allEquals KotlinVisibility.Public
+ }
+ }
+ }
+// @Test
+// fun annotatedProperty() {
+// checkSourceExistsAndVerifyModel(
+// "testdata/properties/annotatedProperty.kt",
+// modelConfig = ModelConfig(
+// analysisPlatform = analysisPlatform,
+// withKotlinRuntime = true
+// )
+// ) { model ->
+// with(model.members.single().members.single()) {
+// Assert.assertEquals(1, annotations.count())
+// with(annotations[0]) {
+// Assert.assertEquals("Strictfp", name)
+// Assert.assertEquals(Content.Empty, content)
+// Assert.assertEquals(NodeKind.Annotation, kind)
+// }
+// }
+// }
+// }
+//
+//}
+}
diff --git a/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt b/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt
new file mode 100644
index 00000000..b3ac7b07
--- /dev/null
+++ b/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt
@@ -0,0 +1,54 @@
+package multiplatform
+
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+
+class BasicMultiplatformTest : AbstractCoreTest() {
+
+ @Test
+ fun dataTestExample() {
+ val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("$testDataDir/jvmMain/")
+ }
+ }
+ }
+
+ testFromData(configuration) {
+ pagesTransformationStage = {
+ assertEquals(6, it.children.firstOrNull()?.children?.count() ?: 0)
+ }
+ }
+ }
+
+ @Test
+ fun inlineTestExample() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/multiplatform/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/multiplatform/Test.kt
+ |package multiplatform
+ |
+ |object Test {
+ | fun test2(str: String): Unit {println(str)}
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesGenerationStage = {
+ assertEquals(3, it.parentMap.size)
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt
new file mode 100644
index 00000000..935b9377
--- /dev/null
+++ b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt
@@ -0,0 +1,126 @@
+package pageMerger
+
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.PageNode
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+
+class PageNodeMergerTest : AbstractCoreTest() {
+
+ /* object SameNameStrategy : DokkaPlugin() {
+ val strategy by extending { CoreExtensions.pageMergerStrategy with SameMethodNamePageMergerStrategy }
+ }
+
+ class DefaultStrategy(val strList: MutableList<String> = mutableListOf()) : DokkaPlugin(), DokkaLogger {
+ val strategy by extending { CoreExtensions.pageMergerStrategy with DefaultPageMergerStrategy(this@DefaultStrategy) }
+
+ override var warningsCount: Int = 0
+ override var errorsCount: Int = 0
+
+ override fun debug(message: String) = TODO()
+
+ override fun info(message: String) = TODO()
+
+ override fun progress(message: String) = TODO()
+
+ override fun warn(message: String) {
+ strList += message
+ }
+
+ override fun error(message: String) = TODO()
+
+ override fun report() = TODO()
+ }
+ */
+
+ @Test
+ fun sameNameStrategyTest() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |fun testT(): Int = 1
+ |fun testT(i: Int): Int = i
+ |
+ |object Test {
+ | fun test(): String = ""
+ | fun test(str: String): String = str
+ |}
+ """.trimMargin(),
+ configuration/*,
+ pluginOverrides = listOf(SameNameStrategy)*/
+ ) {
+ pagesTransformationStage = {
+ val allChildren = it.childrenRec().filterIsInstance<ContentPage>()
+ val testT = allChildren.filter { it.name == "testT" }
+ val test = allChildren.filter { it.name == "test" }
+
+ assertTrue(testT.size == 1) { "There can be only one testT page" }
+ assertTrue(testT.first().dri.size == 2) { "testT page should have 2 DRI, but has ${testT.first().dri.size}" }
+
+ assertTrue(test.size == 1) { "There can be only one test page" }
+ assertTrue(test.first().dri.size == 2) { "test page should have 2 DRI, but has ${test.first().dri.size}" }
+ }
+ }
+ }
+
+ @Disabled("TODO: reenable when we have infrastructure for turning off extensions")
+ @Test
+ fun defaultStrategyTest() {
+ val strList: MutableList<String> = mutableListOf()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |fun testT(): Int = 1
+ |fun testT(i: Int): Int = i
+ |
+ |object Test {
+ | fun test(): String = ""
+ | fun test(str: String): String = str
+ |}
+ """.trimMargin(),
+ configuration/*,
+ pluginOverrides = listOf(DefaultStrategy(strList)) */
+ ) {
+ pagesTransformationStage = { root ->
+ val allChildren = root.childrenRec().filterIsInstance<ContentPage>()
+ val testT = allChildren.filter { it.name == "testT" }
+ val test = allChildren.filter { it.name == "test" }
+
+ assertTrue(testT.size == 1) { "There can be only one testT page" }
+ assertTrue(testT.first().dri.size == 1) { "testT page should have single DRI, but has ${testT.first().dri.size}" }
+
+ assertTrue(test.size == 1) { "There can be only one test page" }
+ assertTrue(test.first().dri.size == 1) { "test page should have single DRI, but has ${test.first().dri.size}" }
+
+ assertTrue(strList.count() == 2) { "Expected 2 warnings, got ${strList.count()}" }
+ }
+ }
+ }
+
+ fun PageNode.childrenRec(): List<PageNode> = listOf(this) + children.flatMap { it.childrenRec() }
+
+}
diff --git a/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt
new file mode 100644
index 00000000..8ab277f1
--- /dev/null
+++ b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt
@@ -0,0 +1,328 @@
+package renderers.html
+
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.SourceRootImpl
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.ContentDivergentGroup
+import org.junit.jupiter.api.Test
+import renderers.*
+import utils.Div
+import utils.Span
+import utils.match
+
+class DivergentTest : HtmlRenderingOnlyTestBase() {
+
+ @Test
+ fun simpleWrappingCase() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ }
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div(Div("a")))))
+ }
+
+ @Test
+ fun noPlatformHintCase() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test"), implicitlySourceSetHinted = false) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ }
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div("a")))
+ }
+
+ @Test
+ fun divergentBetweenSourceSets() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("c")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div(Div("a"), Div("b"), Div("c")))))
+ }
+
+ @Test
+ fun divergentInOneSourceSet() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(js)) {
+ divergent {
+ text("c")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div((Div(Div("abc"))))))
+ }
+
+ @Test
+ fun divergentInAndBetweenSourceSets() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("c")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("d")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(native)) {
+ divergent {
+ text("e")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div(Div("ae"), Div("bd"), Div("c")))))
+ }
+
+ @Test
+ fun divergentInAndBetweenSourceSetsWithGrouping() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ after {
+ text("a+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("c")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("d")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(native)) {
+ divergent {
+ text("e")
+ }
+ after {
+ text("e+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("a"))), "a+"),
+ Div(Div(Span(Div(Div("JS")))), Div(Div(Div("bd"))), "bd+"),
+ Div(Div(Span(Div(Div("JVM")))), Div(Div(Div("c")))),
+ Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("e"))), "e+"),
+ )
+ }
+
+ @Test
+ fun divergentSameBefore() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("b")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Div(
+ Div(
+ Div("ab-"),
+ Span()
+ ),
+ Div(Div(Div("ab")))
+ )
+ )
+ }
+
+ @Test
+ fun divergentSameAfter() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Div(
+ Div(Div(Div("ab"))),
+ "ab+"
+ )
+ )
+ }
+
+ @Test
+ fun divergentGroupedByBeforeAndAfter() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Div(
+ Div(Div("ab-"), Span()),
+ Div(Div(Div("ab"))),
+ "ab+"
+ )
+ )
+ }
+
+ @Test
+ fun divergentDifferentBeforeAndAfter() {
+ val page = TestPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("a-")
+ }
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("b-")
+ }
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Div(Div(Div("a-"), Span()), Div(Div(Div("a"))), "ab+"),
+ Div(Div(Div("b-"), Span()), Div(Div(Div(("b")))), "ab+")
+ )
+ }
+}
diff --git a/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt
new file mode 100644
index 00000000..c0c03998
--- /dev/null
+++ b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt
@@ -0,0 +1,78 @@
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.pages.TextStyle
+import org.junit.jupiter.api.Test
+import renderers.*
+import utils.Div
+import utils.P
+import utils.match
+
+class GroupWrappingTest : HtmlRenderingOnlyTestBase() {
+
+ @Test
+ fun notWrapped() {
+ val page = TestPage {
+ group {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match("abc")
+ }
+
+ @Test
+ fun paragraphWrapped() {
+ val page = TestPage {
+ group(styles = setOf(TextStyle.Paragraph)) {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match(P("ab"), "c")
+ }
+
+ @Test
+ fun blockWrapped() {
+ val page = TestPage {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match(Div("ab"), "c")
+ }
+
+ @Test
+ fun nested() {
+ val page = TestPage {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a")
+ group(styles = setOf(TextStyle.Block)) {
+ group(styles = setOf(TextStyle.Block)) {
+ text("b")
+ text("c")
+ }
+ }
+ text("d")
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match(Div("a", Div(Div("bc")), "d"))
+ }
+
+}
diff --git a/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt
new file mode 100644
index 00000000..b6765fda
--- /dev/null
+++ b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt
@@ -0,0 +1,90 @@
+package renderers.html
+
+import org.jetbrains.dokka.DokkaConfigurationImpl
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.SourceRootImpl
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.renderers.DefaultTabSortingStrategy
+import org.jetbrains.dokka.base.renderers.RootCreator
+import org.jetbrains.dokka.base.resolvers.external.DokkaExternalLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.external.JavadocExternalLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProviderFactory
+import org.jetbrains.dokka.testApi.context.MockContext
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import org.jsoup.nodes.Node
+import org.jsoup.nodes.TextNode
+import renderers.RenderingOnlyTestBase
+import utils.TestOutputWriter
+import renderers.defaultSourceSet
+
+abstract class HtmlRenderingOnlyTestBase : RenderingOnlyTestBase<Element>() {
+
+ protected val js = defaultSourceSet.copy(
+ "root",
+ "JS",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "js"),
+ analysisPlatform = Platform.js,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ protected val jvm = defaultSourceSet.copy(
+ "root",
+ "JVM",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "jvm"),
+
+ analysisPlatform = Platform.jvm,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ protected val native = defaultSourceSet.copy(
+ "root",
+ "NATIVE",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "native"),
+ analysisPlatform = Platform.native,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+
+ val files = TestOutputWriter()
+ override val context = MockContext(
+ DokkaBase().outputWriter to { _ -> files },
+ DokkaBase().locationProviderFactory to ::DefaultLocationProviderFactory,
+ DokkaBase().htmlPreprocessors to { _ -> RootCreator },
+ DokkaBase().externalLocationProviderFactory to { ::JavadocExternalLocationProviderFactory },
+ DokkaBase().externalLocationProviderFactory to { ::DokkaExternalLocationProviderFactory },
+ DokkaBase().tabSortingStrategy to { DefaultTabSortingStrategy() },
+ testConfiguration = DokkaConfigurationImpl(
+ "", null, false, listOf(js, jvm, native), emptyList(), emptyMap(), emptyList(), false
+ )
+ )
+
+ override val renderedContent: Element by lazy {
+ files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select("#content").single()
+ }
+
+ protected fun linesAfterContentTag() =
+ files.contents.getValue("test-page.html").lines()
+ .dropWhile { !it.contains("""<div id="content">""") }
+ .joinToString(separator = "") { it.trim() }
+}
+
+fun Element.match(vararg matchers: Any): Unit =
+ childNodes()
+ .filter { it !is TextNode || it.text().isNotBlank() }
+ .let { it.drop(it.size - matchers.size) }
+ .zip(matchers)
+ .forEach { (n, m) -> m.accepts(n) }
+
+open class Tag(val name: String, vararg val matchers: Any)
+class Div(vararg matchers: Any) : Tag("div", *matchers)
+class P(vararg matchers: Any) : Tag("p", *matchers)
+class Span(vararg matchers: Any) : Tag("span", *matchers)
+
+private fun Any.accepts(n: Node) {
+ when (this) {
+ is String -> assert(n is TextNode && n.text().trim() == this.trim()) { "\"$this\" expected but found: $n" }
+ is Tag -> {
+ assert(n is Element && n.tagName() == name) { "Tag $name expected but found: $n" }
+ if (n is Element && matchers.isNotEmpty()) n.match(*matchers)
+ }
+ else -> throw IllegalArgumentException("$this is not proper matcher")
+ }
+}
diff --git a/plugins/base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt b/plugins/base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt
new file mode 100644
index 00000000..cf7f47e6
--- /dev/null
+++ b/plugins/base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt
@@ -0,0 +1,139 @@
+package renderers.html
+
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.SourceRootImpl
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.pages.TextStyle
+import org.junit.jupiter.api.Test
+import renderers.TestPage
+import renderers.defaultSourceSet
+import renderers.RenderingOnlyTestBase
+import utils.Div
+import utils.match
+
+class SourceSetDependentHintTest : HtmlRenderingOnlyTestBase() {
+
+ private val pl1 = defaultSourceSet.copy(
+ "root",
+ "pl1",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "pl1"),
+ analysisPlatform = Platform.js,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ private val pl2 = defaultSourceSet.copy(
+ "root",
+ "pl2",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "pl2"),
+ analysisPlatform = Platform.jvm,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+ private val pl3 = defaultSourceSet.copy(
+ "root",
+ "pl3",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "pl3"),
+ analysisPlatform = Platform.native,
+ sourceRoots = listOf(SourceRootImpl("pl1"))
+ )
+
+ @Test
+ fun platformIndependentCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a")
+ text("b")
+ text("c")
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("abc"))))
+ }
+
+ @Test
+ fun completelyDivergentCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl2))
+ text("c", sourceSets = setOf(pl3))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("a")), Div(Div("b")), Div(Div("c"))))
+ }
+
+ @Test
+ fun overlappingCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl1, pl2))
+ text("c", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("ab")), Div(Div("bc"))))
+ }
+
+ @Test
+ fun caseThatCanBeSimplified() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("ab"))))
+ }
+
+ @Test
+ fun caseWithGroupBreakingSimplification() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ }
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div(Div("ab"))), Div(Div(Div("a"), "b"))))
+ }
+
+ @Test
+ fun caseWithGroupNotBreakingSimplification() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2)) {
+ group {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ }
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div("ab")))
+ }
+
+ @Test
+ fun partiallyUnifiedCase() {
+ val page = TestPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("a", sourceSets = setOf(pl2))
+ text("b", sourceSets = setOf(pl3))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("a")), Div(Div("b"))))
+ }
+}
diff --git a/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt b/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt
new file mode 100644
index 00000000..4f8a834b
--- /dev/null
+++ b/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt
@@ -0,0 +1,71 @@
+package resourceLinks
+
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.pages.RootPageNode
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jetbrains.dokka.transformers.pages.PageTransformer
+import org.jsoup.Jsoup
+import org.junit.jupiter.api.Test
+import utils.TestOutputWriterPlugin
+
+class ResourceLinksTest : AbstractCoreTest() {
+ class TestResourcesAppenderPlugin(val resources: List<String>) : DokkaPlugin() {
+ class TestResourcesAppender(val resources: List<String>) : PageTransformer {
+ override fun invoke(input: RootPageNode) = input.transformContentPagesTree {
+ it.modified(
+ embeddedResources = it.embeddedResources + resources
+ )
+ }
+ }
+
+ val appender by extending {
+ plugin<DokkaBase>().htmlPreprocessors with TestResourcesAppender(resources)
+ }
+ }
+ @Test
+ fun resourceLinksTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+ val absoluteResources = listOf(
+ "https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css",
+ "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"
+ )
+ val relativeResources = listOf(
+ "test/relativePath.js",
+ "test/relativePath.css"
+ )
+
+ val source =
+ """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ """.trimIndent()
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(TestResourcesAppenderPlugin(absoluteResources + relativeResources), writerPlugin)
+ ) {
+ renderingStage = {
+ root, context -> Jsoup
+ .parse(writerPlugin.writer.contents["root/example.html"])
+ .head()
+ .select("link, script")
+ .let {
+ absoluteResources.forEach {
+ r -> assert(it.`is`("[href=$r], [src=$r]"))
+ }
+ relativeResources.forEach {
+ r -> assert(it.`is`("[href=../$r] , [src=../$r]"))
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt
new file mode 100644
index 00000000..7635ab05
--- /dev/null
+++ b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt
@@ -0,0 +1,175 @@
+package signatures
+
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import org.jsoup.select.Elements
+import org.junit.jupiter.api.Test
+import java.nio.file.Paths
+import utils.TestOutputWriterPlugin
+
+class DivergentSignatureTest : AbstractCoreTest() {
+
+ @Test
+ fun `group { common + jvm + js }`() {
+
+ val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ moduleName = "example"
+ displayName = "js"
+ name = "js"
+ analysisPlatform = "js"
+ sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ sourceSet {
+ moduleName = "example"
+ displayName = "jvm"
+ name = "jvm"
+ analysisPlatform = "jvm"
+ sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ sourceSet {
+ moduleName = "example"
+ displayName = "common"
+ name = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("commonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ }
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testFromData(
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.renderedContent("example/example/-clock/get-time.html")
+
+ assert(content.count() == 1)
+ assert(content.select("[data-filterable-current=example/js example/jvm example/common]").single().brief == "")
+ }
+ }
+ }
+
+ @Test
+ fun `group { common + jvm }, group { js }`() {
+
+ val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ moduleName = "example"
+ displayName = "js"
+ name = "js"
+ analysisPlatform = "js"
+ sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ sourceSet {
+ moduleName = "example"
+ displayName = "jvm"
+ name = "jvm"
+ analysisPlatform = "jvm"
+ sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ sourceSet {
+ moduleName = "example"
+ displayName = "common"
+ name = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("commonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ }
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testFromData(
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.renderedContent("example/example/-clock/get-times-in-millis.html")
+ assert(content.count() == 2)
+ assert(content.select("[data-filterable-current=example/jvm example/common]").single().brief == "Time in minis")
+ assert(content.select("[data-filterable-current=example/js]").single().brief == "JS implementation of getTimeInMillis js" )
+ }
+ }
+ }
+
+ @Test
+ fun `group { js }, group { jvm }, group { js }`() {
+
+ val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ moduleName = "example"
+ displayName = "js"
+ name = "js"
+ analysisPlatform = "js"
+ sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ sourceSet {
+ moduleName = "example"
+ displayName = "jvm"
+ name = "jvm"
+ analysisPlatform = "jvm"
+ sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ sourceSet {
+ moduleName = "example"
+ displayName = "common"
+ name = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("commonMain").map {
+ Paths.get("$testDataDir/$it/kotlin").toString()
+ }
+ }
+ }
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testFromData(
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.renderedContent("example/example/-clock/get-year.html")
+ assert(content.count() == 3)
+ assert(content.select("[data-filterable-current=example/jvm]").single().brief == "JVM custom kdoc jvm")
+ assert(content.select("[data-filterable-current=example/js]").single().brief == "JS custom kdoc js")
+ assert(content.select("[data-filterable-current=example/common]").single().brief == "common")
+ }
+ }
+ }
+
+ private fun TestOutputWriterPlugin.renderedContent(path: String) = writer.contents.getValue(path)
+ .let { Jsoup.parse(it) }.select("#content").single().select("div.divergent-group")
+
+ private val Element.brief: String
+ get() = children().select(".brief-with-platform-tags").text()
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
new file mode 100644
index 00000000..9f2ae435
--- /dev/null
+++ b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt
@@ -0,0 +1,379 @@
+package signatures
+
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jsoup.Jsoup
+import org.junit.jupiter.api.Test
+import utils.*
+
+class SignatureTest : AbstractCoreTest() {
+
+ fun source(signature: String) =
+ """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ | $signature
+ """.trimIndent()
+
+ @Test
+ fun `fun`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("fun simpleFun(): String = \"Celebrimbor\"")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "fun ", A("simpleFun"), "(): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `open fun`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("open fun simpleFun(): String = \"Celebrimbor\"")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "open fun ", A("simpleFun"), "(): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `open suspend fun`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("open suspend fun simpleFun(): String = \"Celebrimbor\"")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "open suspend fun ", A("simpleFun"), "(): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `fun with params`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("fun simpleFun(a: Int, b: Boolean, c: Any): String = \"Celebrimbor\"")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "fun ", A("simpleFun"), "(a: ", A("Int"),
+ ", b: ", A("Boolean"), ", c: ", A("Any"),
+ "): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `fun with function param`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("fun simpleFun(a: (Int) -> String): String = \"Celebrimbor\"")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "fun ", A("simpleFun"), "(a: (", A("Int"),
+ ") -> ", A("String"), "): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `fun with generic param`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("fun <T> simpleFun(): T = \"Celebrimbor\" as T")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "fun <", A("T"), " : ", A("Any"), "?> ", A("simpleFun"), "(): ",
+ A("T"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `fun with generic bounded param`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("fun <T : String> simpleFun(): T = \"Celebrimbor\" as T")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "fun <", A("T"), " : ", A("String"), "> ", A("simpleFun"),
+ "(): ", A("T"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `fun with keywords, params and generic bound`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = source("inline suspend fun <T : String> simpleFun(a: Int, b: String): T = \"Celebrimbor\" as T")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ "inline suspend fun <", A("T"), " : ", A("String"), "> ", A("simpleFun"),
+ "(a: ", A("Int"), ", b: ", A("String"), "): ", A("T"), Span()
+ )
+ }
+ }
+ }
+
+
+ @Test
+ fun `fun with annotation`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ | @MustBeDocumented()
+ | @Target(AnnotationTarget.FUNCTION)
+ | annotation class Marking
+ |
+ | @Marking()
+ | fun simpleFun(): String = "Celebrimbor"
+ """.trimIndent()
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ Div(
+ Div("@", A("Marking"), "()")
+ ),
+ "fun ", A("simpleFun"),
+ "(): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `fun with two annotations`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ | @MustBeDocumented()
+ | @Target(AnnotationTarget.FUNCTION)
+ | annotation class Marking(val msg: String)
+ |
+ | @MustBeDocumented()
+ | @Target(AnnotationTarget.FUNCTION)
+ | annotation class Marking2(val int: Int)
+ |
+ | @Marking("Nenya")
+ | @Marking2(1)
+ | fun simpleFun(): String = "Celebrimbor"
+ """.trimIndent()
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html")
+ .match(
+ Div(
+ Div("@", A("Marking"), "(", Span("msg = ", Span("\"Nenya\"")), Wbr, ")"),
+ Div("@", A("Marking2"), "(", Span("int = ", Span("1")), Wbr, ")")
+ ),
+ "fun ", A("simpleFun"),
+ "(): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `fun with annotation with array`() {
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/test/Test.kt")
+ }
+ }
+ }
+
+ val source = """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ | @MustBeDocumented()
+ | @Target(AnnotationTarget.FUNCTION)
+ | annotation class Marking(val msg: Array<String>)
+ |
+ | @Marking(["Nenya", "Vilya", "Narya"])
+ | @Marking2(1)
+ | fun simpleFun(): String = "Celebrimbor"
+ """.trimIndent()
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/example/simple-fun.html").match(
+ Div(
+ Div("@", A("Marking"), "(", Span("msg = [",
+ Span(Span("\"Nenya\""), ", "), Wbr,
+ Span(Span("\"Vilya\""), ", "), Wbr,
+ Span(Span("\"Narya\"")), Wbr, "]"), Wbr, ")"
+ )
+ ),
+ "fun ", A("simpleFun"),
+ "(): ", A("String"), Span()
+ )
+ }
+ }
+ }
+
+ private fun TestOutputWriter.renderedContent(path: String = "root/example.html") =
+ contents.getValue(path).let { Jsoup.parse(it) }.select("#content")
+ .single().select("div.symbol div.monospace").first()
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt b/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt
new file mode 100644
index 00000000..d8e057da
--- /dev/null
+++ b/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt
@@ -0,0 +1,150 @@
+package transformerBuilders;
+
+import org.jetbrains.dokka.CoreExtensions
+import org.jetbrains.dokka.pages.PageNode
+import org.jetbrains.dokka.pages.RendererSpecificResourcePage
+import org.jetbrains.dokka.pages.RenderingStrategy
+import org.jetbrains.dokka.plugability.DokkaPlugin
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.jetbrains.dokka.transformers.pages.PageTransformer
+import org.jetbrains.dokka.transformers.pages.pageMapper
+import org.jetbrains.dokka.transformers.pages.pageScanner
+import org.jetbrains.dokka.transformers.pages.pageStructureTransformer
+import org.junit.jupiter.api.Test
+
+class PageTransformerBuilderTest : AbstractCoreTest() {
+
+ class ProxyPlugin(transformer: PageTransformer) : DokkaPlugin() {
+ val pageTransformer by extending { CoreExtensions.pageTransformer with transformer }
+ }
+
+ @Test
+ fun scannerTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/transformerBuilder/Test.kt")
+ }
+ }
+ }
+ val list = mutableListOf<String>()
+
+ var orig: PageNode? = null
+
+ testInline(
+ """
+ |/src/main/kotlin/transformerBuilder/Test.kt
+ |package transformerBuilder
+ |
+ |object Test {
+ | fun test2(str: String): Unit {println(str)}
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(ProxyPlugin(pageScanner {
+ list += name
+ }))
+ ) {
+ pagesGenerationStage = {
+ orig = it
+ }
+ pagesTransformationStage = { root ->
+ list.assertCount(4, "Page list: ")
+ orig?.let { root.assertTransform(it) }
+ }
+ }
+ }
+
+ @Test
+ fun mapperTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/transformerBuilder/Test.kt")
+ }
+ }
+ }
+
+ var orig: PageNode? = null
+
+ testInline(
+ """
+ |/src/main/kotlin/transformerBuilder/Test.kt
+ |package transformerBuilder
+ |
+ |object Test {
+ | fun test2(str: String): Unit {println(str)}
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(ProxyPlugin(pageMapper {
+ modified(name = name + "2")
+ }))
+ ) {
+ pagesGenerationStage = {
+ orig = it
+ }
+ pagesTransformationStage = {
+ it.let { root ->
+ root.name.assertEqual("root2", "Root name: ")
+ orig?.let {
+ root.assertTransform(it) { node -> node.modified(name = node.name + "2") }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun structureTransformerTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin/transformerBuilder/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/transformerBuilder/Test.kt
+ |package transformerBuilder
+ |
+ |object Test {
+ | fun test2(str: String): Unit {println(str)}
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(ProxyPlugin(pageStructureTransformer {
+ val ch = children.first()
+ modified(
+ children = listOf(
+ ch,
+ RendererSpecificResourcePage("test", emptyList(), RenderingStrategy.DoNothing)
+ )
+ )
+ }))
+ ) {
+ pagesTransformationStage = { root ->
+ root.children.assertCount(2, "Root children: ")
+ root.children.first().name.assertEqual("transformerBuilder")
+ root.children[1].name.assertEqual("test")
+ }
+ }
+ }
+
+ private fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") =
+ assert(count() == n) { "${prefix}Expected $n, got ${count()}" }
+
+ private fun <T> T.assertEqual(expected: T, prefix: String = "") = assert(this == expected) {
+ "${prefix}Expected $expected, got $this"
+ }
+
+ private fun PageNode.assertTransform(expected: PageNode, block: (PageNode) -> PageNode = { it }): Unit = this.let {
+ it.name.assertEqual(block(expected).name)
+ it.children.zip(expected.children).forEach { (g, e) ->
+ g.name.assertEqual(block(e).name)
+ g.assertTransform(e, block)
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt
new file mode 100644
index 00000000..5197afc6
--- /dev/null
+++ b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt
@@ -0,0 +1,403 @@
+package transformers
+
+import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.doc.*
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Test
+import matchers.content.*
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.kotlin.utils.addToStdlib.assertedCast
+
+class CommentsToContentConverterTest {
+ private val converter = DocTagToContentConverter
+
+ private fun executeTest(
+ docTag:DocTag,
+ match: ContentMatcherBuilder<ContentComposite>.() -> Unit
+ ) {
+ val dci = DCI(
+ setOf(
+ DRI("kotlin", "Any")
+ ),
+ ContentKind.Comment
+ )
+ converter.buildContent(
+ Li(
+ listOf(
+ docTag
+ )
+ ),
+ dci,
+ emptySet()
+ ).single().assertNode(match)
+ }
+
+ @Test
+ fun `simple text`() {
+ val docTag = P(listOf(Text("This is simple test of string Next line")))
+ executeTest(docTag) {
+ +"This is simple test of string Next line"
+ }
+ }
+
+ @Test
+ fun `simple text with new line`() {
+ val docTag = P(
+ listOf(
+ Text("This is simple test of string"),
+ Br,
+ Text("Next line")
+ )
+ )
+ executeTest(docTag) {
+ +"This is simple test of string"
+ node<ContentBreakLine>()
+ +"Next line"
+ }
+ }
+
+ @Test
+ fun `paragraphs`() {
+ val docTag = P(
+ listOf(
+ P(listOf(Text("Paragraph number one"))),
+ P(listOf(Text("Paragraph"), Br, Text("number two")))
+ )
+ )
+ executeTest(docTag) {
+ +"Paragraph number one"
+ +"Paragraph"
+ node<ContentBreakLine>()
+ +"number two"
+ }
+ }
+
+ @Test
+ fun `unordered list with empty lines`() {
+ val docTag = Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("list item 1 continue 1"))))),
+ Li(listOf(P(listOf(Text("list item 2"), Br, Text("continue 2")))))
+ )
+ )
+ executeTest(docTag) {
+ node<ContentList> {
+ group {
+ +"list item 1 continue 1"
+ }
+ group {
+ +"list item 2"
+ node<ContentBreakLine>()
+ +"continue 2"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `nested list`() {
+ val docTag = P(
+ listOf(
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("Outer first Outer next line"))))),
+ Li(listOf(P(listOf(Text("Outer second"))))),
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("Middle first Middle next line"))))),
+ Li(listOf(P(listOf(Text("Middle second"))))),
+ Ul(
+ listOf(
+ Li(listOf(P(listOf(Text("Inner first Inner next line")))))
+ )
+ ),
+ Li(listOf(P(listOf(Text("Middle third")))))
+ )
+ ),
+ Li(listOf(P(listOf(Text("Outer third")))))
+ )
+ ),
+ P(listOf(Text("New paragraph")))
+ )
+ )
+ executeTest(docTag) {
+ node<ContentList> {
+ group { +"Outer first Outer next line" }
+ group { +"Outer second" }
+ node<ContentList> {
+ group { +"Middle first Middle next line" }
+ group { +"Middle second" }
+ node<ContentList> {
+ group { +"Inner first Inner next line" }
+ }
+ group { +"Middle third" }
+ }
+ group { +"Outer third" }
+ }
+ +"New paragraph"
+ }
+ }
+
+ @Test
+ fun `header and paragraphs`() {
+ val docTag = P(
+ listOf(
+ H1(listOf(Text("Header 1"))),
+ P(listOf(Text("Following text"))),
+ P(listOf(Text("New paragraph")))
+ )
+ )
+ executeTest(docTag) {
+ header(1) { +"Header 1" }
+ +"Following text"
+ +"New paragraph"
+ }
+ }
+
+ @Test
+ fun `header levels`() {
+ val docTag = P(
+ listOf(
+ H1(listOf(Text("Header 1"))),
+ P(listOf(Text("Text 1"))),
+ H2(listOf(Text("Header 2"))),
+ P(listOf(Text("Text 2"))),
+ H3(listOf(Text("Header 3"))),
+ P(listOf(Text("Text 3"))),
+ H4(listOf(Text("Header 4"))),
+ P(listOf(Text("Text 4"))),
+ H5(listOf(Text("Header 5"))),
+ P(listOf(Text("Text 5"))),
+ H6(listOf(Text("Header 6"))),
+ P(listOf(Text("Text 6")))
+ )
+ )
+ executeTest(docTag) {
+ header(1) {+"Header 1"}
+ +"Text 1"
+ header(2) {+"Header 2"}
+ +"Text 2"
+ header(3) {+"Header 3"}
+ +"Text 3"
+ header(4) {+"Header 4"}
+ +"Text 4"
+ header(5) {+"Header 5"}
+ +"Text 5"
+ header(6) {+"Header 6"}
+ +"Text 6"
+ }
+ }
+
+ @Test
+ fun `block quotes`() {
+ val docTag = P(
+ listOf(
+ BlockQuote(
+ listOf(
+ P(
+ listOf(
+ Text("Blockquotes are very handy in email to emulate reply text. This line is part of the same quote.")
+ )
+ )
+ )
+ ),
+ P(listOf(Text("Quote break."))),
+ BlockQuote(
+ listOf(
+ P(listOf(Text("Quote")))
+ )
+ )
+ )
+ )
+ executeTest(docTag) {
+ node<ContentCodeBlock> {
+ +"Blockquotes are very handy in email to emulate reply text. This line is part of the same quote."
+ }
+ +"Quote break."
+ node<ContentCodeBlock> {
+ +"Quote"
+ }
+ }
+ }
+
+ @Test
+ fun `nested block quotes`() {
+ val docTag = P(
+ listOf(
+ BlockQuote(
+ listOf(
+ P(listOf(Text("text 1 text 2"))),
+ BlockQuote(
+ listOf(
+ P(listOf(Text("text 3 text 4")))
+ )
+ ),
+ P(listOf(Text("text 5")))
+ )
+ ),
+ P(listOf(Text("Quote break."))),
+ BlockQuote(
+ listOf(
+ P(listOf(Text("Quote")))
+ )
+ )
+ )
+ )
+ executeTest(docTag) {
+ node<ContentCodeBlock> {
+ +"text 1 text 2"
+ node<ContentCodeBlock> {
+ +"text 3 text 4"
+ }
+ +"text 5"
+ }
+ +"Quote break."
+ node<ContentCodeBlock> {
+ +"Quote"
+ }
+ }
+ }
+
+ @Test
+ fun `multiline code`() {
+ val docTag = P(
+ listOf(
+ CodeBlock(
+ listOf(
+ Text("val x: Int = 0"), Br,
+ Text("val y: String = \"Text\""), Br, Br,
+ Text(" val z: Boolean = true"), Br,
+ Text("for(i in 0..10) {"), Br,
+ Text(" println(i)"), Br,
+ Text("}")
+ ),
+ mapOf("lang" to "kotlin")
+ ),
+ P(listOf(Text("Sample text")))
+ )
+ )
+ executeTest(docTag) {
+ node<ContentCodeBlock> {
+ +"val x: Int = 0"
+ node<ContentBreakLine>()
+ +"val y: String = \"Text\""
+ node<ContentBreakLine>()
+ node<ContentBreakLine>()
+ +" val z: Boolean = true"
+ node<ContentBreakLine>()
+ +"for(i in 0..10) {"
+ node<ContentBreakLine>()
+ +" println(i)"
+ node<ContentBreakLine>()
+ +"}"
+ }
+ +"Sample text"
+ }
+ }
+
+ @Test
+ fun `inline link`() {
+ val docTag = P(
+ listOf(
+ A(
+ listOf(Text("I'm an inline-style link")),
+ mapOf("href" to "https://www.google.com")
+ )
+ )
+ )
+ executeTest(docTag) {
+ link {
+ +"I'm an inline-style link"
+ check {
+ assertEquals(
+ assertedCast<ContentResolvedLink> { "Link should be resolved" }.address,
+ "https://www.google.com"
+ )
+ }
+ }
+ }
+ }
+
+
+
+ @Test
+ fun `ordered list`() {
+ val docTag =
+ Ol(
+ listOf(
+ Li(
+ listOf(
+ P(listOf(Text("test1"))),
+ P(listOf(Text("test2"))),
+ )
+ ),
+ Li(
+ listOf(
+ P(listOf(Text("test3"))),
+ P(listOf(Text("test4"))),
+ )
+ )
+ )
+ )
+ executeTest(docTag) {
+ node<ContentList> {
+ group {
+ +"test1"
+ +"test2"
+ }
+ group {
+ +"test3"
+ +"test4"
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `nested ordered list`() {
+ val docTag = P(
+ listOf(
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Outer first Outer next line"))))),
+ Li(listOf(P(listOf(Text("Outer second"))))),
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Middle first Middle next line"))))),
+ Li(listOf(P(listOf(Text("Middle second"))))),
+ Ol(
+ listOf(
+ Li(listOf(P(listOf(Text("Inner first Inner next line")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text("Middle third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ Li(listOf(P(listOf(Text("Outer third")))))
+ ),
+ mapOf("start" to "1")
+ ),
+ P(listOf(Text("New paragraph")))
+ )
+ )
+ executeTest(docTag) {
+ node<ContentList> {
+ group { +"Outer first Outer next line" }
+ group { +"Outer second" }
+ node<ContentList> {
+ group { +"Middle first Middle next line" }
+ group { +"Middle second" }
+ node<ContentList> {
+ +"Inner first Inner next line"
+ }
+ group { +"Middle third" }
+ }
+ group { +"Outer third" }
+ }
+ +"New paragraph"
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt
new file mode 100644
index 00000000..72948372
--- /dev/null
+++ b/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt
@@ -0,0 +1,919 @@
+package transformers
+
+import org.jetbrains.dokka.PackageOptionsImpl
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.*
+import org.junit.jupiter.api.Disabled
+import org.junit.jupiter.api.Test
+
+
+class ReportUndocumentedTransformerTest : AbstractCoreTest() {
+ @Test
+ fun `undocumented class gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |class X
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport(Regex("init"))
+ assertSingleUndocumentedReport(Regex("""sample/X/"""))
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented non-public class does not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |internal class X
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented function gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |/** Documented */
+ |class X {
+ | fun x()
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("X"))
+ assertSingleUndocumentedReport(Regex("X/x"))
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented property gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |/** Documented */
+ |class X {
+ | val x: Int = 0
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("X"))
+ assertSingleUndocumentedReport(Regex("X/x"))
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented primary constructor does not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |/** Documented */
+ |class X(private val x: Int) {
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `data class component functions do not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |/** Documented */
+ |data class X(val x: Int) {
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport(Regex("component"))
+ assertNumberOfUndocumentedReports(1)
+ }
+ }
+ }
+
+ @Disabled
+ @Test
+ fun `undocumented secondary constructor gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |/** Documented */
+ |class X {
+ | constructor(unit: Unit) : this()
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("X"))
+ assertSingleUndocumentedReport(Regex("X.*init.*Unit"))
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented inherited function does not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |/** Documented */
+ |open class A {
+ | fun a() = Unit
+ |}
+ |
+ |/** Documented */
+ |class B : A()
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport(Regex("B"))
+ assertSingleUndocumentedReport(Regex("A.*a"))
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented inherited property does not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |/** Documented */
+ |open class A {
+ | val a = Unit
+ |}
+ |
+ |/** Documented */
+ |class B : A()
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport(Regex("B"))
+ assertSingleUndocumentedReport(Regex("A.*a"))
+ }
+ }
+ }
+
+ @Test
+ fun `overridden function does not get reported when super is documented`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |import kotlin.Exception
+ |
+ |/** Documented */
+ |open class A {
+ | /** Documented */
+ | fun a() = Unit
+ |}
+ |
+ |/** Documented */
+ |class B : A() {
+ | override fun a() = throw Exception()
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `overridden property does not get reported when super is documented`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |import kotlin.Exception
+ |
+ |/** Documented */
+ |open class A {
+ | /** Documented */
+ | open val a = 0
+ |}
+ |
+ |/** Documented */
+ |class B : A() {
+ | override val a = 1
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `report disabled by source set`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = false
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |class X
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `report enabled by package configuration`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ perPackageOptions += packageOptions(
+ prefix = "sample",
+ reportUndocumented = true,
+ )
+ reportUndocumented = false
+ sourceRoots = listOf("src/main/kotlin/Test.kt")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/Test.kt
+ |package sample
+ |
+ |class X
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("X"))
+ }
+ }
+ }
+
+ @Test
+ fun `report enabled by more specific package configuration`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ perPackageOptions += packageOptions(
+ prefix = "sample",
+ reportUndocumented = false,
+ )
+ perPackageOptions += packageOptions(
+ prefix = "sample.enabled",
+ reportUndocumented = true,
+ )
+ reportUndocumented = false
+ sourceRoots = listOf("src/main/kotlin/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/sample/disabled/Disabled.kt
+ |package sample.disabled
+ |class Disabled
+ |
+ |/src/main/kotlin/sample/enabled/Enabled.kt
+ |package sample.enabled
+ |class Enabled
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("Enabled"))
+ assertNumberOfUndocumentedReports(1)
+ }
+ }
+ }
+
+ @Test
+ fun `report disabled by more specific package configuration`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ perPackageOptions += packageOptions(
+ prefix = "sample",
+ reportUndocumented = true,
+ )
+ perPackageOptions += packageOptions(
+ prefix = "sample.disabled",
+ reportUndocumented = false,
+ )
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/kotlin/")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/sample/disabled/Disabled.kt
+ |package sample.disabled
+ |class Disabled
+ |
+ |/src/main/kotlin/sample/enabled/Enabled.kt
+ |package sample.enabled
+ |class Enabled
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("Enabled"))
+ assertNumberOfUndocumentedReports(1)
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatform undocumented class gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ val commonMain = sourceSet {
+ reportUndocumented = true
+ analysisPlatform = Platform.common.toString()
+ name = "commonMain"
+ displayName = "commonMain"
+ sourceRoots = listOf("src/commonMain/kotlin")
+ }
+
+ sourceSet {
+ reportUndocumented = true
+ analysisPlatform = Platform.jvm.toString()
+ name = "jvmMain"
+ displayName = "jvmMain"
+ sourceRoots = listOf("src/jvmMain/kotlin")
+ dependentSourceSets = setOf(commonMain.sourceSetID)
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/commonMain/kotlin/sample/Common.kt
+ |package sample
+ |expect class X
+ |
+ |/src/jvmMain/kotlin/sample/JvmMain.kt
+ |package sample
+ |actual class X
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNumberOfUndocumentedReports(2, Regex("X"))
+ assertSingleUndocumentedReport(Regex("X.*jvmMain"))
+ assertSingleUndocumentedReport(Regex("X.*commonMain"))
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatform undocumented class does not get reported if expect is documented`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ val commonMain = sourceSet {
+ reportUndocumented = true
+ analysisPlatform = Platform.common.toString()
+ name = "commonMain"
+ displayName = "commonMain"
+ sourceRoots = listOf("src/commonMain/kotlin")
+ }
+
+ sourceSet {
+ reportUndocumented = true
+ analysisPlatform = Platform.jvm.toString()
+ name = "jvmMain"
+ displayName = "jvmMain"
+ sourceRoots = listOf("src/jvmMain/kotlin")
+ dependentSourceSets = setOf(commonMain.sourceSetID)
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/commonMain/kotlin/sample/Common.kt
+ |package sample
+ |/** Documented */
+ |expect class X
+ |
+ |/src/jvmMain/kotlin/sample/JvmMain.kt
+ |package sample
+ |actual class X
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNumberOfUndocumentedReports(0)
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatform undocumented function gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ val commonMain = sourceSet {
+ reportUndocumented = true
+ analysisPlatform = Platform.common.toString()
+ name = "commonMain"
+ displayName = "commonMain"
+ sourceRoots = listOf("src/commonMain/kotlin")
+ }
+
+ sourceSet {
+ reportUndocumented = true
+ analysisPlatform = Platform.jvm.toString()
+ name = "jvmMain"
+ displayName = "jvmMain"
+ sourceRoots = listOf("src/jvmMain/kotlin")
+ dependentSourceSets = setOf(commonMain.sourceSetID)
+ }
+
+ sourceSet {
+ reportUndocumented = true
+ analysisPlatform = Platform.native.toString()
+ name = "macosMain"
+ displayName = "macosMain"
+ sourceRoots = listOf("src/macosMain/kotlin")
+ dependentSourceSets = setOf(commonMain.sourceSetID)
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/commonMain/kotlin/sample/Common.kt
+ |package sample
+ |expect fun x()
+ |
+ |/src/macosMain/kotlin/sample/MacosMain.kt
+ |package sample
+ |/** Documented */
+ |actual fun x() = Unit
+ |
+ |/src/jvmMain/kotlin/sample/JvmMain.kt
+ |package sample
+ |actual fun x() = Unit
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNumberOfUndocumentedReports(2)
+ assertSingleUndocumentedReport(Regex("x.*commonMain"))
+ assertSingleUndocumentedReport(Regex("x.*jvmMain"))
+ }
+ }
+ }
+
+ @Test
+ fun `java undocumented class gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/Test.java
+ |package sample
+ |public class Test { }
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport(Regex("init"))
+ assertSingleUndocumentedReport(Regex("""Test"""))
+ assertNumberOfUndocumentedReports(1)
+ }
+ }
+ }
+
+ @Test
+ fun `java undocumented non-public class does not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/Test.java
+ |package sample
+ |class Test { }
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `java undocumented constructor does not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/Test.java
+ |package sample
+ |/** Documented */
+ |public class Test {
+ | public Test() {
+ | }
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `java undocumented method gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/X.java
+ |package sample
+ |/** Documented */
+ |public class X {
+ | public void x { }
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("X"))
+ assertSingleUndocumentedReport(Regex("X.*x"))
+ assertNumberOfUndocumentedReports(1)
+ }
+ }
+ }
+
+ @Test
+ fun `java undocumented property gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/X.java
+ |package sample
+ |/** Documented */
+ |public class X {
+ | public int x = 0;
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("X"))
+ assertSingleUndocumentedReport(Regex("X.*x"))
+ assertNumberOfUndocumentedReports(1)
+ }
+ }
+ }
+
+ @Test
+ fun `java undocumented inherited method gets reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/Super.java
+ |package sample
+ |/** Documented */
+ |public class Super {
+ | public void x() {}
+ |}
+ |
+ |/src/main/java/sample/X.java
+ |package sample
+ |/** Documented */
+ |public class X extends Super {
+ | public void x() {}
+ |}
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertSingleUndocumentedReport(Regex("X"))
+ assertSingleUndocumentedReport(Regex("X.*x"))
+ assertSingleUndocumentedReport(Regex("Super.*x"))
+ assertNumberOfUndocumentedReports(2)
+ }
+ }
+ }
+
+ @Test
+ fun `java documented inherited method does not get reported`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/Super.java
+ |package sample
+ |/** Documented */
+ |public class Super {
+ | /** Documented */
+ | public void x() {}
+ |}
+ |
+ |/src/main/java/sample/X.java
+ |package sample
+ |/** Documented */
+ |public class X extends Super {
+ |
+ |}
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ @Test
+ fun `java overridden function does not get reported when super is documented`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ reportUndocumented = true
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/Super.java
+ |package sample
+ |/** Documented */
+ |public class Super {
+ | /** Documented */
+ | public void x() {}
+ |}
+ |
+ |/src/main/java/sample/X.java
+ |package sample
+ |/** Documented */
+ |public class X extends Super {
+ | @Override
+ | public void x() {}
+ |}
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = {
+ assertNoUndocumentedReport()
+ }
+ }
+ }
+
+ private fun assertNumberOfUndocumentedReports(expectedReports: Int, regex: Regex = Regex(".")) {
+ val reports = logger.warnMessages
+ .filter { it.startsWith("Undocumented:") }
+ val matchingReports = reports
+ .filter { it.contains(regex) }
+
+ assertEquals(
+ expectedReports, matchingReports.size,
+ "Expected $expectedReports report of documented code ($regex).\n" +
+ "Found matching reports: $matchingReports\n" +
+ "Found reports: $reports"
+ )
+ }
+
+ private fun assertSingleUndocumentedReport(regex: Regex) {
+ assertNumberOfUndocumentedReports(1, regex)
+ }
+
+ private fun assertNoUndocumentedReport(regex: Regex) {
+ assertNumberOfUndocumentedReports(0, regex)
+ }
+
+ private fun assertNoUndocumentedReport() {
+ assertNoUndocumentedReport(Regex("."))
+ }
+
+ private fun packageOptions(
+ prefix: String,
+ reportUndocumented: Boolean?,
+ includeNonPublic: Boolean = true,
+ skipDeprecated: Boolean = false,
+ suppress: Boolean = false
+ ) = PackageOptionsImpl(
+ prefix = prefix,
+ reportUndocumented = reportUndocumented,
+ includeNonPublic = includeNonPublic,
+ skipDeprecated = skipDeprecated,
+ suppress = suppress
+ )
+}
diff --git a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
new file mode 100644
index 00000000..b0754429
--- /dev/null
+++ b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt
@@ -0,0 +1,87 @@
+package translators
+
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.Test
+
+class DefaultDescriptorToDocumentableTranslatorTest : AbstractCoreTest() {
+
+ @Test
+ fun `data class kdocs over generated methods`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/sample/XD.kt
+ |package sample
+ |/**
+ | * But the fat Hobbit, he knows. Eyes always watching.
+ | */
+ |data class XD(val xd: String) {
+ | /**
+ | * But the fat Hobbit, he knows. Eyes always watching.
+ | */
+ | fun custom(): String = ""
+ |
+ | /**
+ | * Memory is not what the heart desires. That is only a mirror.
+ | */
+ | override fun equals(other: Any?): Boolean = true
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ assert(module.documentationOf("XD", "copy") == "")
+ assert(module.documentationOf("XD", "equals") == "Memory is not what the heart desires. That is only a mirror.")
+ assert(module.documentationOf("XD", "hashCode") == "")
+ assert(module.documentationOf("XD", "toString") == "")
+ assert(module.documentationOf("XD", "custom") == "But the fat Hobbit, he knows. Eyes always watching.")
+ }
+ }
+ }
+
+ @Test
+ fun `simple class kdocs`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/kotlin")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/kotlin/sample/XD.kt
+ |package sample
+ |/**
+ | * But the fat Hobbit, he knows. Eyes always watching.
+ | */
+ |class XD(val xd: String) {
+ | /**
+ | * But the fat Hobbit, he knows. Eyes always watching.
+ | */
+ | fun custom(): String = ""
+ |
+ | /**
+ | * Memory is not what the heart desires. That is only a mirror.
+ | */
+ | override fun equals(other: Any?): Boolean = true
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ assert(module.documentationOf("XD", "custom") == "But the fat Hobbit, he knows. Eyes always watching.")
+ assert(module.documentationOf("XD", "equals") == "Memory is not what the heart desires. That is only a mirror.")
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
new file mode 100644
index 00000000..eb682b14
--- /dev/null
+++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt
@@ -0,0 +1,144 @@
+package translators
+
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.model.doc.Description
+import org.jetbrains.dokka.model.doc.Text
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Test
+
+class DefaultPsiToDocumentableTranslatorTest : AbstractCoreTest() {
+
+ @Test
+ fun `method overriding two documented classes picks closest class documentation`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/BaseClass1.java
+ |package sample
+ |public class BaseClass1 {
+ | /** B1 */
+ | void x() { }
+ |}
+ |
+ |/src/main/java/sample/BaseClass2.java
+ |package sample
+ |public class BaseClass2 extends BaseClass1 {
+ | /** B2 */
+ | void x() { }
+ |}
+ |
+ |/src/main/java/sample/X.java
+ |package sample
+ |public class X extends BaseClass2 {
+ | void x() { }
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val documentationOfFunctionX = module.documentationOf("X", "x")
+ assertTrue(
+ "B2" in documentationOfFunctionX,
+ "Expected nearest super method documentation to be parsed as documentation. " +
+ "Documentation: $documentationOfFunctionX"
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `method overriding class and interface picks class documentation`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/BaseClass1.java
+ |package sample
+ |public class BaseClass1 {
+ | /** B1 */
+ | void x() { }
+ |}
+ |
+ |/src/main/java/sample/Interface1.java
+ |package sample
+ |public interface Interface1 {
+ | /** I1 */
+ | void x() {}
+ |}
+ |
+ |/src/main/java/sample/X.java
+ |package sample
+ |public class X extends BaseClass1 implements Interface1 {
+ | void x() { }
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val documentationOfFunctionX = module.documentationOf("X", "x")
+ assertTrue(
+ "B1" in documentationOfFunctionX,
+ "Expected documentation of superclass being prioritized over interface " +
+ "Documentation: $documentationOfFunctionX"
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `method overriding two classes picks closest documented class documentation`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/main/java")
+ }
+ }
+ }
+
+ testInline(
+ """
+ |/src/main/java/sample/BaseClass1.java
+ |package sample
+ |public class BaseClass1 {
+ | /** B1 */
+ | void x() { }
+ |}
+ |
+ |/src/main/java/sample/BaseClass2.java
+ |package sample
+ |public class BaseClass2 extends BaseClass1 {
+ | void x() {}
+ |}
+ |
+ |/src/main/java/sample/X.java
+ |package sample
+ |public class X extends BaseClass2 {
+ | void x() { }
+ |}
+ """.trimMargin(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val documentationOfFunctionX = module.documentationOf("X", "x")
+ assertTrue(
+ "B1" in documentationOfFunctionX,
+ "Expected Documentation \"B1\", found: \"$documentationOfFunctionX\""
+ )
+ }
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/translators/utils.kt b/plugins/base/src/test/kotlin/translators/utils.kt
new file mode 100644
index 00000000..96d3035a
--- /dev/null
+++ b/plugins/base/src/test/kotlin/translators/utils.kt
@@ -0,0 +1,16 @@
+package translators
+
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.model.doc.Description
+import org.jetbrains.dokka.model.doc.Text
+
+fun DModule.documentationOf(className: String, functionName: String): String {
+ return (packages.single()
+ .classlikes.single { it.name == className }
+ .functions.single { it.name == functionName }
+ .documentation.values.singleOrNull()
+ ?.children?.singleOrNull()
+ .run { this as? Description }
+ ?.root?.children?.single() as? Text)
+ ?.body.orEmpty()
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/utils/JsoupUtils.kt b/plugins/base/src/test/kotlin/utils/JsoupUtils.kt
new file mode 100644
index 00000000..e8c7838d
--- /dev/null
+++ b/plugins/base/src/test/kotlin/utils/JsoupUtils.kt
@@ -0,0 +1,29 @@
+package utils
+
+import org.jsoup.nodes.Element
+import org.jsoup.nodes.Node
+import org.jsoup.nodes.TextNode
+
+fun Element.match(vararg matchers: Any): Unit =
+ childNodes()
+ .filter { it !is TextNode || it.text().isNotBlank() }
+ .let { it.drop(it.size - matchers.size) }
+ .zip(matchers)
+ .forEach { (n, m) -> m.accepts(n) }
+
+open class Tag(val name: String, vararg val matchers: Any)
+class Div(vararg matchers: Any) : Tag("div", *matchers)
+class P(vararg matchers: Any) : Tag("p", *matchers)
+class Span(vararg matchers: Any) : Tag("span", *matchers)
+class A(vararg matchers: Any) : Tag("a", *matchers)
+object Wbr : Tag("wbr")
+private fun Any.accepts(n: Node) {
+ when (this) {
+ is String -> assert(n is TextNode && n.text().trim() == this.trim()) { "\"$this\" expected but found: $n" }
+ is Tag -> {
+ assert(n is Element && n.tagName() == name) { "Tag $name expected but found: $n" }
+ if (n is Element && matchers.isNotEmpty()) n.match(*matchers)
+ }
+ else -> throw IllegalArgumentException("$this is not proper matcher")
+ }
+} \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/utils/ModelUtils.kt b/plugins/base/src/test/kotlin/utils/ModelUtils.kt
new file mode 100644
index 00000000..87a9c802
--- /dev/null
+++ b/plugins/base/src/test/kotlin/utils/ModelUtils.kt
@@ -0,0 +1,37 @@
+package utils
+
+import org.jetbrains.dokka.DokkaConfigurationImpl
+import org.jetbrains.dokka.model.DModule
+import org.jetbrains.dokka.plugability.DokkaPlugin
+
+abstract class AbstractModelTest(val path: String? = null, val pkg: String) : ModelDSL(), AssertDSL {
+
+ fun inlineModelTest(
+ query: String,
+ platform: String = "jvm",
+ prependPackage: Boolean = true,
+ cleanupOutput: Boolean = true,
+ pluginsOverrides: List<DokkaPlugin> = emptyList(),
+ configuration: DokkaConfigurationImpl? = null,
+ block: DModule.() -> Unit
+ ) {
+ val testConfiguration = configuration ?: dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = platform
+ }
+ }
+ }
+ val prepend = path.let { p -> p?.let { "|$it\n" } ?: "" } + if (prependPackage) "|package $pkg" else ""
+
+ testInline(
+ query = ("$prepend\n$query").trim().trimIndent(),
+ configuration = testConfiguration,
+ cleanupOutput = cleanupOutput,
+ pluginOverrides = pluginsOverrides
+ ) {
+ documentablesTransformationStage = block
+ }
+ }
+}
diff --git a/plugins/base/src/test/kotlin/utils/TestUtils.kt b/plugins/base/src/test/kotlin/utils/TestUtils.kt
new file mode 100644
index 00000000..bd0e1fe2
--- /dev/null
+++ b/plugins/base/src/test/kotlin/utils/TestUtils.kt
@@ -0,0 +1,79 @@
+package utils
+
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.model.doc.*
+import org.jetbrains.dokka.model.doc.P
+import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest
+import org.junit.jupiter.api.Assertions.assertTrue
+import kotlin.collections.orEmpty
+
+@DslMarker
+annotation class TestDSL
+
+@TestDSL
+abstract class ModelDSL : AbstractCoreTest() {
+ operator fun Documentable?.div(name: String): Documentable? =
+ this?.children?.find { it.name == name }
+
+ inline fun <reified T : Documentable> Documentable?.cast(): T =
+ (this as? T).assertNotNull()
+}
+
+@TestDSL
+interface AssertDSL {
+ infix fun Any?.equals(other: Any?) = this.assertEqual(other)
+ infix fun Collection<Any>?.allEquals(other: Any?) =
+ this?.also { c -> c.forEach { it equals other } } ?: run { assert(false) { "Collection is empty" } }
+ infix fun <T> Collection<T>?.exists(e: T) {
+ assertTrue(this.orEmpty().isNotEmpty(), "Collection cannot be null or empty")
+ assertTrue(this!!.any{it == e}, "Collection doesn't contain $e")
+ }
+
+ infix fun <T> Collection<T>?.counts(n: Int) = this.orEmpty().assertCount(n)
+
+ infix fun <T> T?.notNull(name: String): T = this.assertNotNull(name)
+
+ fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") =
+ assert(count() == n) { "${prefix}Expected $n, got ${count()}" }
+
+ fun <T> T?.assertEqual(expected: T, prefix: String = "") = assert(this == expected) {
+ "${prefix}Expected $expected, got $this"
+ }
+}
+
+inline fun <reified T : Any> Any?.assertIsInstance(name: String): T =
+ this.let { it as? T } ?: throw AssertionError("$name should not be null")
+
+fun TagWrapper.text(): String = when (val t = this) {
+ is NamedTagWrapper -> "${t.name}: [${t.root.text()}]"
+ else -> t.root.text()
+}
+
+fun DocTag.text(): String = when (val t = this) {
+ is Text -> t.body
+ is Code -> t.children.joinToString("\n") { it.text() }
+ is P -> t.children.joinToString(separator = "\n") { it.text() }
+ else -> t.toString()
+}
+
+fun <T : Documentable> T?.comments(): String = docs().map { it.text() }
+ .joinToString(separator = "\n") { it }
+
+fun <T> T?.assertNotNull(name: String = ""): T = this ?: throw AssertionError("$name should not be null")
+
+fun <T : Documentable> T?.docs() = this?.documentation.orEmpty().values.flatMap { it.children }
+
+val DClass.supers
+ get() = supertypes.flatMap { it.component2() }
+
+val Bound.name: String?
+ get() = when (this) {
+ is Nullable -> inner.name
+ is OtherParameter -> name
+ is PrimitiveJavaType -> name
+ is TypeConstructor -> dri.classNames
+ is JavaObject -> "Object"
+ is Void -> "void"
+ is Dynamic -> "dynamic"
+ is UnresolvedBound -> "<ERROR CLASS>"
+ } \ No newline at end of file
diff --git a/plugins/base/src/test/kotlin/utils/contentUtils.kt b/plugins/base/src/test/kotlin/utils/contentUtils.kt
new file mode 100644
index 00000000..7fcd8e89
--- /dev/null
+++ b/plugins/base/src/test/kotlin/utils/contentUtils.kt
@@ -0,0 +1,243 @@
+package utils
+
+import matchers.content.*
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.pages.ContentGroup
+import kotlin.text.Typography.nbsp
+
+//TODO: Try to unify those functions after update to 1.4
+fun ContentMatcherBuilder<*>.functionSignature(
+ annotations: Map<String, Set<String>>,
+ visibility: String,
+ modifier: String,
+ keywords: Set<String>,
+ name: String,
+ returnType: String? = null,
+ vararg params: Pair<String, ParamAttributes>
+) =
+ platformHinted {
+ bareSignature(annotations, visibility, modifier, keywords, name, returnType, *params)
+ }
+
+fun ContentMatcherBuilder<*>.bareSignature(
+ annotations: Map<String, Set<String>>,
+ visibility: String,
+ modifier: String,
+ keywords: Set<String>,
+ name: String,
+ returnType: String? = null,
+ vararg params: Pair<String, ParamAttributes>
+) = group {
+ annotations.entries.forEach {
+ group {
+ unwrapAnnotation(it)
+ }
+ }
+ +("$visibility $modifier ${keywords.joinToString("") { "$it " }} fun")
+ link { +name }
+ +"("
+ params.forEachIndexed { id, (n, t) ->
+
+ t.annotations.forEach {
+ unwrapAnnotation(it)
+ }
+ t.keywords.forEach {
+ +it
+ }
+
+ +"$n:"
+ group { link { +(t.type) } }
+ if (id != params.lastIndex)
+ +", "
+ }
+ +")"
+ if (returnType != null) {
+ +(": ")
+ group {
+ link {
+ +(returnType)
+ }
+ }
+ }
+}
+
+fun ContentMatcherBuilder<*>.functionSignatureWithReceiver(
+ annotations: Map<String, Set<String>>,
+ visibility: String?,
+ modifier: String?,
+ keywords: Set<String>,
+ receiver: String,
+ name: String,
+ returnType: String? = null,
+ vararg params: Pair<String, ParamAttributes>
+) =
+ platformHinted {
+ bareSignatureWithReceiver(annotations, visibility, modifier, keywords, receiver, name, returnType, *params)
+ }
+
+fun ContentMatcherBuilder<*>.bareSignatureWithReceiver(
+ annotations: Map<String, Set<String>>,
+ visibility: String?,
+ modifier: String?,
+ keywords: Set<String>,
+ receiver: String,
+ name: String,
+ returnType: String? = null,
+ vararg params: Pair<String, ParamAttributes>
+) = group { // TODO: remove it when double wrapping for signatures will be resolved
+ annotations.entries.forEach {
+ group {
+ unwrapAnnotation(it)
+ }
+ }
+ +("$visibility $modifier ${keywords.joinToString("") { "$it " }} fun")
+ group {
+ link { +receiver }
+ }
+ +"."
+ link { +name }
+ +"("
+ params.forEachIndexed { id, (n, t) ->
+
+ t.annotations.forEach {
+ unwrapAnnotation(it)
+ }
+ t.keywords.forEach {
+ +it
+ }
+
+ +"$n:"
+ group { link { +(t.type) } }
+ if (id != params.lastIndex)
+ +", "
+ }
+ +")"
+ if (returnType != null) {
+ +(": ")
+ group {
+ link {
+ +(returnType)
+ }
+ }
+ }
+}
+
+fun ContentMatcherBuilder<*>.propertySignature(
+ annotations: Map<String, Set<String>>,
+ visibility: String,
+ modifier: String,
+ keywords: Set<String>,
+ preposition: String,
+ name: String,
+ type: String? = null
+) {
+ group {
+ header { +"Package test" }
+ skipAllNotMatching()
+ }
+ group {
+ group {
+ skipAllNotMatching()
+ header { +"Properties" }
+ table {
+ group {
+ link { +name }
+ platformHinted {
+ group {
+ annotations.entries.forEach {
+ group {
+ unwrapAnnotation(it)
+ }
+ }
+ +("$visibility $modifier ${keywords.joinToString("") { "$it " }} $preposition")
+ link { +name }
+ if (type != null) {
+ +(": ")
+ group {
+ link {
+ +(type)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+fun ContentMatcherBuilder<*>.typealiasSignature(name: String, expressionTarget: String) {
+ group {
+ header { +"Package test" }
+ skipAllNotMatching()
+ }
+ group {
+ group {
+ skipAllNotMatching()
+ header { +"Types" }
+ table {
+ group {
+ link { +name }
+ divergentGroup {
+ divergentInstance {
+ group {
+ group {
+ group {
+ group {
+ +"typealias "
+ group {
+ link { +name }
+ skipAllNotMatching()
+ }
+ +" = "
+ group {
+ link { +expressionTarget }
+ }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ skipAllNotMatching()
+ }
+ skipAllNotMatching()
+ }
+ }
+}
+
+fun ContentMatcherBuilder<*>.pWrapped(text: String) =
+ group {// TODO: remove it when double wrapping for descriptions will be resolved
+ group { +text }
+ }
+
+fun ContentMatcherBuilder<*>.unnamedTag(tag: String, content: ContentMatcherBuilder<ContentGroup>.() -> Unit) =
+ group {
+ header(4) { +tag }
+ group { content() }
+ }
+
+fun ContentMatcherBuilder<*>.unwrapAnnotation(elem: Map.Entry<String, Set<String>>) {
+ group {
+ +"@"
+ link { +elem.key }
+ +"("
+ elem.value.forEach {
+ group {
+ +("$it = ")
+ skipAllNotMatching()
+ }
+ }
+ +")"
+ }
+}
+
+data class ParamAttributes(
+ val annotations: Map<String, Set<String>>,
+ val keywords: Set<String>,
+ val type: String
+)