aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/plugin-kotlin-as-java/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'dokka-subprojects/plugin-kotlin-as-java/src/test')
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt548
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt129
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt170
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt190
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt60
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt71
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt618
-rw-r--r--dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt137
8 files changed, 1923 insertions, 0 deletions
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt
new file mode 100644
index 00000000..cba5e9ef
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/CompanionAsJavaTest.kt
@@ -0,0 +1,548 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.*
+import kotlin.test.*
+
+private const val COMPANION_NAME = "C"
+
+class CompanionAsJavaTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `empty companion object should not be rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {}
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only jvmField should not be rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmField val jvmFieldProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion property with jvmField should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmField val jvmFieldProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassProperty = parentClass.properties.firstOrNull { it.name == "jvmFieldProp" }
+ assertNotNull(parentClassProperty, "Parent class should contain the companion jvmField property")
+ assertIsStatic(parentClassProperty)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only const should not be rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val constProp: Int = 0
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion property with const should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val constProp: Int = 0
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassProperty = parentClass.properties.firstOrNull { it.name == "constProp" }
+ assertNotNull(parentClassProperty, "Parent class should contain the companion const property")
+ assertIsStatic(parentClassProperty)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only lateinit not rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | lateinit var lateInitProp: String
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion property with lateinit should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | lateinit var lateInitProp: String
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassProperty = parentClass.properties.firstOrNull { it.name == "lateInitProp" }
+ assertNotNull(parentClassProperty, "Parent class should contain the companion lateinit property")
+ assertIsStatic(parentClassProperty)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with only jvmStatic fun not rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic fun staticFun(): String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionNotRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion function with JvmStatic should be static`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic fun staticFun(): String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ val parentClassFunction = parentClass.functions.firstOrNull { it.name == "staticFun" }
+ assertNotNull(parentClassFunction, "Parent class should contains the companion jvmStatic function")
+ assertIsStatic(parentClassFunction)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with nested classes is rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic
+ | fun staticFun1(): String = ""
+ |
+ | const val CONST_VAL: Int = 100
+ |
+ | class NestedClass
+ | object NestedObject
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+
+ val classLikes = parentClass.companion?.classlikes
+ assertNotNull(classLikes)
+ assertEquals(2, classLikes.size,
+ "Classlike list should contains nested class and object")
+ assertTrue(classLikes.any { it.name == "NestedClass" })
+ assertTrue(classLikes.any { it.name == "NestedObject" })
+
+ }
+ }
+ }
+
+ @Test
+ fun `companion object with supertype is rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Parent
+ |interface IParent
+ |class MyClass {
+ | companion object $COMPANION_NAME : Parent(), IParent {
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+ }
+ }
+ }
+
+ @Test
+ fun `companion object rendered for own properties`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmField
+ | val jvmField: String = ""
+ | const val contVal: Int = 0
+ | lateinit var lateInit: String
+ |
+ | val rendered: Int = TODO()
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+
+ val properties = parentClass.companion?.properties
+
+ assertNotNull(properties)
+ assertEquals(2, properties.size) // including INSTANCE
+ assertTrue(properties.any { it.name == "rendered" })
+ assertTrue(properties.none { it.name == "jvmField1" })
+ assertTrue(properties.none { it.name == "contVal" })
+ assertTrue(properties.none { it.name == "lateInit" })
+ }
+ }
+ }
+
+ @Test
+ fun `companion object rendered for own functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | @JvmStatic
+ | fun staticFun(): String = ""
+ |
+ | fun renderedFun(): String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertCompanionRendered(parentClass)
+
+ val functions = parentClass.companion?.functions
+
+ assertNotNull(functions)
+ assertEquals(1, functions.size)
+ assertTrue(functions.any { it.name == "renderedFun" })
+ assertTrue(functions.none { it.name == "staticFun" })
+ }
+ }
+ }
+
+ @Test
+ fun `companion const value should be rendered as public by default`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val constProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertEquals(
+ JavaVisibility.Public,
+ parentClass.properties.firstOrNull()?.visibility?.values?.first()
+ )
+ assertNull(parentClass.findFunction("constProp"), "There is no getter for the cont field")
+ }
+ }
+ }
+
+ @Test
+ fun `companion const value should preserve Java modifier`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | protected const val constProp: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ documentedVisibilities = setOf(
+ org.jetbrains.dokka.DokkaConfiguration.Visibility.PUBLIC,
+ org.jetbrains.dokka.DokkaConfiguration.Visibility.PROTECTED
+ )
+ }
+ }
+ },
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertEquals(
+ JavaVisibility.Protected,
+ parentClass.properties.firstOrNull()?.visibility?.values?.first()
+ )
+ assertNull(parentClass.findFunction("constProp"), "There is no getter for the cont field")
+ }
+ }
+ }
+
+ @Test
+ fun `companion lateinit value should be rendered as public by default`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | lateinit var lateInitProp: String
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertEquals(
+ JavaVisibility.Public,
+ parentClass.properties.firstOrNull()?.visibility?.values?.first()
+ )
+ assertNull(parentClass.findFunction("lateInitProp"), "There is no getter for the cont field")
+ }
+ }
+ }
+
+ @Test
+ fun `named companion instance property should be rendered if companion rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | var property: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertNotNull(parentClass.properties.any { it.name == COMPANION_NAME })
+ }
+ }
+ }
+
+ @Test
+ fun `default named companion instance property should be rendered if companion rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object {
+ | var property: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertTrue(parentClass.properties.any { it.name == "Companion" })
+ }
+ }
+ }
+
+ @Test
+ fun `companion instance property should be hidden if companion not rendered`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class MyClass {
+ | companion object $COMPANION_NAME {
+ | const val property: String = ""
+ | }
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val parentClass = module.findClass("MyClass")
+
+ assertTrue(parentClass.properties.none { it.name == COMPANION_NAME })
+ }
+ }
+ }
+}
+
+private fun DModule.findClass(name: String) = packages.flatMap { it.classlikes }
+ .firstOrNull { it.name == name } as DClass
+
+private fun DClass.findFunction(name: String) = functions.firstOrNull { it.name.contains(name, ignoreCase = true) }
+
+private fun assertCompanionRendered(parentClass: DClass) {
+ assertNotNull(parentClass.companion, "Companion should not be null")
+ assertTrue(
+ parentClass.classlikes.any { it.name == COMPANION_NAME },
+ "Companion should be in classlikes list"
+ )
+}
+
+private fun assertCompanionNotRendered(parentClass: DClass) {
+ assertNull(parentClass.companion, "Companion should be null")
+ assertTrue(
+ parentClass.classlikes.none { it.name == COMPANION_NAME },
+ "Companion should not be in classlikes list"
+ )
+}
+
+private fun assertIsStatic(property: DProperty) {
+ val extra = property.extra[AdditionalModifiers]
+ assertNotNull(extra, "extra for property is present")
+ assertTrue(
+ extra.content.values.contains(setOf(ExtraModifiers.JavaOnlyModifiers.Static)),
+ "Property contains extra modifier static"
+ )
+}
+
+private fun assertIsStatic(function: DFunction) {
+ val extra = function.extra[AdditionalModifiers]
+ assertNotNull(extra, "extra for property is present")
+ assertTrue(
+ extra.content.values.contains(setOf(ExtraModifiers.JavaOnlyModifiers.Static)),
+ "Function contains extra modifier static"
+ )
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt
new file mode 100644
index 00000000..bdea1cb4
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/DRITranslationTest.kt
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.DEnum
+import kotlin.test.Test
+import kotlin.test.assertTrue
+
+class DRITranslationTest : BaseAbstractTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle nested classes`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class A {
+ | class B(val x: String)
+ |}
+ |class C {
+ | class B(val x: String)
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedClasslikesDRIs = module.packages.flatMap { it.classlikes }.flatMap { it.classlikes }.map { it.dri }
+ val driRegex = "[AC]\\.B".toRegex()
+
+ nestedClasslikesDRIs.forEach { dri ->
+ assertTrue(driRegex.matches(dri.classNames.toString()))
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle interface methods`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |interface A {
+ | fun b()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedFunctionDRI = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.filter { it.name == "b" }.map { it.dri }.single()
+
+ assertTrue(nestedFunctionDRI.classNames == "A")
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle object methods`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |object A {
+ | fun b() {}
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedFunctionDRI = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.filter { it.name == "b" }.map { it.dri }.single()
+
+ assertTrue(nestedFunctionDRI.classNames == "A")
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle enum functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |enum class A(private val x: Int) {
+ | X(0);
+ | fun b() = x
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val nestedFunctionDRI = (module.packages.single().classlikes.single() as DEnum).functions.filter { it.name == "b" }.map { it.dri }.single()
+
+ assertTrue(nestedFunctionDRI.classNames == "A")
+ }
+ }
+ }
+
+ @Test
+ fun `should correctly handle nested classes' constructors`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class A {
+ | class B(val x: String)
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val constructorDRI = (module.packages.flatMap { it.classlikes }.flatMap { it.classlikes }.single() as DClass).constructors.single().dri
+ assertTrue(constructorDRI.classNames == "A.B")
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt
new file mode 100644
index 00000000..6167e13a
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmFieldTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.AdditionalModifiers
+import org.jetbrains.dokka.model.ExtraModifiers
+import org.jetbrains.dokka.model.JavaVisibility
+import kotlin.test.*
+
+class JvmFieldTest : BaseAbstractTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should keep properties annotated with JvmField as properties`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class SampleClass(@JvmField val property: String, val otherProperty: String)
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertNotNull(classLike.properties.firstOrNull { it.name == "property" })
+ assertEquals(
+ listOf("getOtherProperty"),
+ classLike.functions.map { it.name })
+ }
+ }
+ }
+
+ @Test
+ fun `should work for top-level property`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmField
+ |val property: String = TODO()
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertNotNull(classLike.properties.firstOrNull { it.name == "property" })
+ assertEquals(
+ emptyList(),
+ classLike.functions.map { it.name })
+ }
+ }
+ }
+
+ @Test
+ fun `properties without JvmName should be kept private`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |class SampleClass(val property: String)
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertEquals(JavaVisibility.Private, classLike.properties.firstOrNull()?.visibility?.values?.first())
+ assertNotNull(classLike.functions.firstOrNull { it.name.startsWith("get") })
+ assertEquals(
+ JavaVisibility.Public,
+ classLike.functions.first { it.name.startsWith("get") }.visibility.values.first()
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `object jvmfield property should have no getters`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |object MyObject {
+ | @JvmField
+ | val property: String = TODO()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ val property = classLike.properties.singleOrNull { it.name == "property" }
+ assertNotNull(property)
+ assertEquals(
+ emptyList(),
+ classLike.functions.map { it.name }
+ )
+ assertNull(property.getter)
+ assertNull(property.setter)
+ }
+ }
+ }
+
+ @Test
+ fun `enum jvmfield property should have no getters`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |enum class MyEnum {
+ | ITEM;
+ |
+ | @JvmField
+ | val property: String = TODO()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ val property = classLike.properties.singleOrNull { it.name == "property" }
+ assertNotNull(property)
+ assertEquals(
+ emptyList(),
+ classLike.functions
+ .filter{ it.name.contains("property", ignoreCase = true) }
+ .map { it.name }
+ )
+ assertNull(property.getter)
+ assertNull(property.setter)
+ }
+ }
+ }
+
+
+ @Test
+ fun `object jvmfield property should be static`(){
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |object MyObject {
+ | @JvmField
+ | val property: String = TODO()
+ |}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ val property = classLike.properties.singleOrNull { it.name == "property" }
+ assertNotNull(property)
+
+ val extra = property.extra[AdditionalModifiers]
+ assertNotNull(extra, "Additional modifiers for property are exist")
+ assertTrue(extra.content.values.contains(setOf(ExtraModifiers.JavaOnlyModifiers.Static)),
+ "Extra modifiers contains static")
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt
new file mode 100644
index 00000000..7a087fb7
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmNameTest.kt
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.links.Callable
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.TypeConstructor
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.isJvmName
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+class JvmNameTest : BaseAbstractTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should change name for class containing top level function`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedClassLikeDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ )
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertEquals(expectedClassLikeDri, classLike.dri)
+ assertEquals("CustomJvmName", classLike.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should change name for top level function`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |@JvmName("jvmSample")
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedFunctionDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ callable = Callable(
+ "jvmSample",
+ receiver = null,
+ params = emptyList()
+ )
+ )
+ val function = module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first()
+ assertEquals(expectedFunctionDri, function.dri)
+ assertEquals("jvmSample", function.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should change name of a setter for top level property`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |@get:JvmName("xd")
+ |@set:JvmName("asd")
+ |var property: String
+ | get() = ""
+ | set(value) {}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedSetterDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ callable = Callable(
+ "asd",
+ receiver = null,
+ //Todo this is bad, this should be a type in java, look at the bytecode
+ params = listOf(TypeConstructor("kotlin.String", emptyList()))
+ )
+ )
+ val function =
+ module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first { it.name == "asd" }
+ assertEquals(expectedSetterDri, function.dri)
+ assertEquals("asd", function.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should change name of a getter for top level property`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |@file:JvmName("CustomJvmName")
+ |package kotlinAsJavaPlugin
+ |@get:JvmName("xd")
+ |@set:JvmName("asd")
+ |var property: String
+ | get() = ""
+ | set(value) {}
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedGetterDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "CustomJvmName",
+ callable = Callable(
+ "xd",
+ receiver = null,
+ params = emptyList()
+ )
+ )
+ val function =
+ module.packages.flatMap { it.classlikes }.flatMap { it.functions }.first { it.name == "xd" }
+ assertEquals(expectedGetterDri, function.dri)
+ assertEquals("xd", function.name)
+ }
+ }
+ }
+
+ @Test
+ fun `should leave the name as default if annotation is not provided`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val expectedClassLikeDri = DRI(
+ packageName = "kotlinAsJavaPlugin",
+ classNames = "SampleKt",
+ )
+ val classLike = module.packages.flatMap { it.classlikes }.first()
+ assertEquals(expectedClassLikeDri, classLike.dri)
+ assertEquals("SampleKt", classLike.name)
+ }
+ }
+ }
+
+ @Test
+ fun `jvmName extra should be removed after the name swap`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmName("CustomJvmName")
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val classLike = module.packages.flatMap { it.classlikes }.first() as DClass
+ assertNull(
+ classLike.extra[Annotations]?.directAnnotations?.flatMap { it.value }
+ ?.map { it.dri }
+ ?.firstOrNull { it.isJvmName() }
+ )
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt
new file mode 100644
index 00000000..1d6f4698
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmOverloadsTest.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class JvmOverloadsTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should generate multiple functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmOverloads
+ |fun sample(a: Int = 0, b: String, c: Int = 0): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(3, functions.size)
+ assertEquals(3, functions[0].parameters.size)
+ assertEquals(2, functions[1].parameters.size)
+ assertEquals(1, functions[2].parameters.size)
+ }
+ }
+ }
+
+ @Test
+ fun `should do nothing if there is no default values`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmOverloads
+ |fun sample(a: Int, b: String, c: Int): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(1, functions.size)
+ assertEquals(3, functions[0].parameters.size)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt
new file mode 100644
index 00000000..3de48da7
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/JvmSyntheticTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class JvmSyntheticTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `should not include synthetic functions`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@JvmSynthetic
+ |fun synthetic(): String = ""
+ |fun sample(): String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(1, functions.size)
+ assertEquals("sample", functions[0].name)
+ }
+ }
+ }
+
+ @Test
+ fun `should check synthetic on method fields, getters and setters`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/sample.kt
+ |package kotlinAsJavaPlugin
+ |@get:JvmSynthetic
+ |var synthetic: String = ""
+ |
+ |@set:JvmSynthetic
+ |var synthetic2: String = ""
+ |
+ |@JvmSynthetic
+ |var synthetic3: String = ""
+ |
+ |var sample: String = ""
+ """.trimMargin(),
+ configuration,
+ ) {
+ documentablesTransformationStage = { module ->
+ val functions = module.packages.flatMap { it.classlikes }.flatMap { it.functions }
+ assertEquals(4, functions.size)
+ assertEquals("setSynthetic", functions[0].name)
+ assertEquals("getSynthetic2", functions[1].name)
+ assertEquals("getSample", functions[2].name)
+ assertEquals("setSample", functions[3].name)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt
new file mode 100644
index 00000000..93d5c1b5
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaPluginTest.kt
@@ -0,0 +1,618 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import matchers.content.*
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.jdk
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.GenericTypeConstructor
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.pages.*
+import signatures.Parameter
+import signatures.Parameters
+import signatures.firstSignature
+import signatures.renderedContent
+import utils.A
+import utils.TestOutputWriterPlugin
+import utils.match
+import kotlin.test.*
+
+class KotlinAsJavaPluginTest : BaseAbstractTest() {
+
+ @Test
+ fun `top-level functions should be generated`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |object TestObj {}
+ |
+ |fun testFL(l: List<String>) = l
+ |fun testF() {}
+ |fun testF2(i: Int) = i
+ |fun testF3(to: TestObj) = to
+ |fun <T : Char> testF4(t: T) = listOf(t)
+ |val testV = 1
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val content = (root.children.single().children.first { it.name == "TestKt" } as ContentPage).content
+
+ val functionRows = content.findTableWithKind(kind = ContentKind.Functions).children
+ functionRows.assertCount(6)
+
+ val propRows = content.findTableWithKind(kind = ContentKind.Properties).children
+ propRows.assertCount(1)
+ }
+ }
+ }
+
+ private fun ContentNode.findTableWithKind(kind: ContentKind): ContentNode = dfs { node ->
+ node is ContentTable && node.dci.kind === kind
+ }.let { assertNotNull(it, "the table with kind $kind") }
+
+ @Test
+ fun topLevelWithClassTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Test {
+ | fun testFC() {}
+ | val testVC = 1
+ |}
+ |
+ |fun testF(i: Int) = i
+ |val testV = 1
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val contentList = root.children
+ .flatMap { it.children<ContentPage>() }
+
+ contentList.find {it.name == "Test"}.apply {
+ assertNotNull(this)
+ content.findTableWithKind(ContentKind.Functions).children.assertCount(2)
+ content.findTableWithKind(ContentKind.Properties).children.assertCount(1)
+ }
+ contentList.find {it.name == "TestKt"}.apply {
+ assertNotNull(this)
+ content.findTableWithKind(ContentKind.Functions).children.assertCount(2)
+ content.findTableWithKind(ContentKind.Properties).children.assertCount(1)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun kotlinAndJavaTest() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |fun testF(i: Int) = i
+ |/src/main/kotlin/kotlinAsJavaPlugin/TestJ.java
+ |package kotlinAsJavaPlugin;
+ |
+ |public class TestJ {
+ | public int testF(int i) { return i; }
+ |}
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val classes = root.children.first().children.associateBy { it.name }
+ classes.values.assertCount(2, "Class count: ")
+
+ classes["TestKt"].let {
+ it?.children.orEmpty().assertCount(1, "(Kotlin) TestKt members: ")
+ it!!.children.first()
+ .let { assertEquals("testF", it.name, "(Kotlin) Expected method name: testF, got: ${it.name}") }
+ }
+
+ classes["TestJ"].let {
+ it?.children.orEmpty().assertCount(2, "(Java) TestJ members: ") // constructor + method
+ it!!.children.map { it.name }
+ .let {
+ assertTrue(
+ it.containsAll(
+ setOf(
+ "testF",
+ "TestJ"
+ )
+ ),
+ "(Java) Expected method name: testF, got: $it"
+ )
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `public kotlin properties should have a getter with same visibilities`(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Test {
+ | public val publicProperty: String = ""
+ |}
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesTransformationStage = { rootPageNode ->
+ val propertyGetter = rootPageNode.dfs { it is MemberPageNode && it.name == "getPublicProperty" } as? MemberPageNode
+ assertNotNull(propertyGetter)
+ propertyGetter.content.assertNode {
+ group {
+ header(1) {
+ +"getPublicProperty"
+ }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ group {
+ +"public final "
+ group {
+ link {
+ +"String"
+ }
+ }
+ link {
+ +"getPublicProperty"
+ }
+ +"()"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `java properties should keep its modifiers`(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/TestJ.java
+ |package kotlinAsJavaPlugin;
+ |
+ |public class TestJ {
+ | public Int publicProperty = 1;
+ |}
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val testClass = root.dfs { it.name == "TestJ" } as? ClasslikePageNode
+ assertNotNull(testClass)
+ (testClass.content as ContentGroup).children.last().children.last().assertNode {
+ group {
+ header(2){
+ +"Properties"
+ }
+ table {
+ group {
+ link {
+ +"publicProperty"
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ group {
+ group {
+ +"public Int"
+ link {
+ +"publicProperty"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `koltin interfaces and classes should be split to extends and implements`(){
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |open class A { }
+ |interface B
+ |class C : A(), B
+ """.trimMargin(),
+ configuration,
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val testClass = root.dfs { it.name == "C" } as? ClasslikePageNode
+ assertNotNull(testClass)
+ testClass.content.assertNode {
+ group {
+ header(expectedLevel = 1) {
+ +"C"
+ }
+ platformHinted {
+ group {
+ +"public final class "
+ link {
+ +"C"
+ }
+ +" extends "
+ group {
+ link {
+ +"A"
+ }
+ }
+ +" implements "
+ group {
+ link {
+ +"B"
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ private fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") =
+ assertEquals(n, count(), "${prefix}Expected $n, got ${count()}")
+
+ @Test
+ fun `typealias`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(DokkaConfiguration.ExternalDocumentationLink.jdk(8))
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |typealias XD = Int
+ |class ABC {
+ | fun someFun(xd: XD): Int = 1
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-a-b-c/some-fun.html").firstSignature().match(
+ "public final ", A("Integer"), A("someFun"), "(", Parameters(
+ Parameter(A("Integer"), "xd")
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `typealias with generic`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |typealias XD<B, A> = Map<A, B>
+ |
+ |class ABC {
+ | fun someFun(xd: XD<Int, String>) = 1
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-a-b-c/some-fun.html").firstSignature().match(
+ "public final ", A("Integer"), A("someFun"), "(", Parameters(
+ Parameter(A("Map"), "<", A("String"), ", ", A("Integer"), "> xd"),
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `const in top level`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |const val FIRST = "String"
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ assertNull(writerPlugin.writer.contents["root/kotlinAsJavaPlugin/-test-kt/get-f-i-r-s-t.html"])
+ }
+ }
+ }
+
+ @Test
+ fun `function in top level`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |fun sample(a: Int) = ""
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-test-kt/sample.html").firstSignature().match(
+ "public final static ", A("String"), A("sample"), "(", Parameters(
+ Parameter(A("Integer"), "a"),
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should render primary kotlin constructor as a java constructor`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |class Test(val xd: Int)
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ pagesGenerationStage = { root ->
+ val content = root.children
+ .flatMap { it.children<ContentPage>() }
+ .map { it.content }.single().mainContents
+
+ val text = content.single { it is ContentHeader }.children
+ .single() as ContentText
+
+ assertEquals("Constructors", text.text)
+ }
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-test/-test.html").firstSignature().match(
+ A("Test"), A("Test"), "(", Parameters(
+ Parameter(A("Integer"), "xd")
+ ), ")", ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ /**
+ * Kotlin Int becomes java int. Java int cannot be annotated in source, but Kotlin Int can be.
+ * This is paired with DefaultDescriptorToDocumentableTranslatorTest.`Java primitive annotations work`()
+ *
+ * This test currently does not do anything because Kotlin.Int currently becomes java.lang.Integer not primitive int
+ */
+ @Test
+ fun `Java primitive annotations work`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |@MustBeDocumented
+ |@Target(AnnotationTarget.TYPE)
+ |annotation class Hello()
+ |fun bar(): @Hello() Int
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ documentablesTransformationStage = { module ->
+ val type = module.packages.single()
+ .classlikes.first { it.name == "TestKt" }
+ .functions.single()
+ .type as GenericTypeConstructor
+ assertEquals(
+ Annotations.Annotation(DRI("kotlinAsJavaPlugin", "Hello"), emptyMap()),
+ type.extra[Annotations]?.directAnnotations?.values?.single()?.single()
+ )
+ // A bug; the GenericTypeConstructor cast should fail and this should be a PrimitiveJavaType
+ assertEquals("java.lang/Integer///PointingToDeclaration/", type.dri.toString())
+ }
+ }
+ }
+
+ @Test
+ fun `Java function should keep its access modifier`(){
+ val className = "Test"
+ val accessModifier = "public"
+ val methodName = "method"
+
+ val testClassQuery = """
+ |/src/main/kotlin/kotlinAsJavaPlugin/${className}.java
+ |package kotlinAsJavaPlugin;
+ |
+ |public class $className {
+ | $accessModifier void ${methodName}() {
+ |
+ | }
+ |}
+ """.trimMargin()
+
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ testClassQuery,
+ configuration,
+ pluginOverrides = listOf(writerPlugin),
+ cleanupOutput = true
+ ) {
+ renderingStage = { _, _ ->
+ val methodDocumentation = "root/kotlinAsJavaPlugin/-${className.toLowerCase()}/${methodName}.html"
+
+ writerPlugin.writer.renderedContent(methodDocumentation)
+ .firstSignature()
+ .match(
+ "$accessModifier void ", A(methodName), "()",
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+}
+
+private val ContentNode.mainContents: List<ContentNode>
+ get() = (this as ContentGroup).children
+ .filterIsInstance<ContentGroup>()
+ .single { it.dci.kind == ContentKind.Main }.children[0].let { it.children[0] }.children
diff --git a/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt
new file mode 100644
index 00000000..25312810
--- /dev/null
+++ b/dokka-subprojects/plugin-kotlin-as-java/src/test/kotlin/kotlinAsJavaPlugin/KotlinAsJavaSignatureTest.kt
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinAsJavaPlugin
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.jdk
+import signatures.firstSignature
+import signatures.renderedContent
+import signatures.signature
+import utils.*
+import kotlin.test.Test
+
+class KotlinAsJavaSignatureTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ externalDocumentationLinks = listOf(
+ DokkaConfiguration.ExternalDocumentationLink.jdk(8),
+ stdlibExternalDocumentationLink
+ )
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ }
+ }
+
+ @Suppress("SameParameterValue")
+ private fun source(signature: String) =
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ | $signature
+ """.trimIndent()
+
+ @Test
+ fun `fun with definitely non-nullable types as java`() {
+ val source = source("fun <T> elvisLike(x: T, y: T & Any): T & Any = x ?: y")
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val signature = writerPlugin.writer.renderedContent("root/kotlinAsJavaPlugin/-test-kt/elvis-like.html").firstSignature()
+ signature.match(
+ "public final static ", Span("T"), A("elvisLike"),
+ "<T extends ", A("Any"), ">(",
+ Span(
+ Span(Span(), " x, "),
+ Span(Span(), " y")
+ ),
+ ")",
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should display annotations`() {
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ """
+ |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt
+ |package kotlinAsJavaPlugin
+ |
+ |@MustBeDocumented
+ |annotation class OnClass
+ |
+ |@MustBeDocumented
+ |annotation class OnMethod
+ |
+ |@MustBeDocumented
+ |annotation class OnParameter
+ |
+ |@Target(AnnotationTarget.TYPE)
+ |@MustBeDocumented
+ |annotation class OnType
+ |
+ |@Target(AnnotationTarget.TYPE_PARAMETER)
+ |@MustBeDocumented
+ |annotation class OnTypeParameter
+ |
+ |@OnClass
+ |class Clazz<@OnTypeParameter T : @OnType Any> {
+ | @OnMethod
+ | fun <@OnTypeParameter T : @OnType Any> withParams(@OnParameter str1: String, str2: String): Boolean {
+ | return str1 == str2
+ | }
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val signatures = writerPlugin.writer
+ .renderedContent("root/kotlinAsJavaPlugin/-clazz/index.html")
+ .signature()
+
+ val classSignature = signatures[0]
+ classSignature.match(
+ Div(Div("@", A("OnClass"), "()")),
+ "public final class ", A("Clazz"),
+ // <@OnTypeParameter() T extends @OnType() Object>
+ "<", Span("@", A("OnTypeParameter"), "() "), "T extends ", Span("@", A("OnType"), "() "), A("Object"), ">",
+ ignoreSpanWithTokenStyle = true
+ )
+
+ val functionSignature = signatures[2]
+ functionSignature.match(
+ Div(Div("@", A("OnMethod"), "()")),
+ "public final ", A("Boolean"), A("withParams"),
+ // <@OnTypeParameter() T extends @OnType() Object>
+ "<", Span("@", A("OnTypeParameter"), "() "), "T extends ", Span("@", A("OnType"), "() "), A("Any"), ">(",
+ Span(
+ Span(
+ Span("@", A("OnParameter"), "() "),
+ A("String"), "str1, "
+ ),
+ Span(
+ A("String"), "str2"
+ )
+ ), ")",
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+}