From c44bf5487bd32f90a4576859548f1db0e9355a07 Mon Sep 17 00:00:00 2001 From: Vadim Mishenev Date: Mon, 21 Feb 2022 23:06:59 +0300 Subject: Add option to merge implicit expect-actual declarations (#2316) * Add option to merge implicit expect-actual declarations * Merge entries, constructors * Fix StdLib integration test * Add doc --- .../content/functions/ContentForBriefTest.kt | 10 +- .../content/functions/ContentForConstructors.kt | 2 +- .../locationProvider/DokkaLocationProviderTest.kt | 5 +- .../MergeImplicitExpectActualDeclarationsTest.kt | 352 +++++++++++++++++++++ 4 files changed, 359 insertions(+), 10 deletions(-) create mode 100644 plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt (limited to 'plugins/base/src/test/kotlin') diff --git a/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt b/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt index 7d8a169b..50f9e357 100644 --- a/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt +++ b/plugins/base/src/test/kotlin/content/functions/ContentForBriefTest.kt @@ -1,11 +1,11 @@ package content.functions -import org.junit.Assert.assertEquals import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.dokka.links.TypeConstructor import org.jetbrains.dokka.model.DClass import org.jetbrains.dokka.model.dfs import org.jetbrains.dokka.pages.* +import org.junit.Assert.assertEquals import org.junit.jupiter.api.Test import kotlin.test.assertNull @@ -60,7 +60,7 @@ class ContentForBriefTest : BaseAbstractTest() { testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable @@ -84,7 +84,7 @@ class ContentForBriefTest : BaseAbstractTest() { testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable @@ -108,7 +108,7 @@ class ContentForBriefTest : BaseAbstractTest() { testInline(codeWithDocumentedParameter, testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable @@ -319,7 +319,7 @@ class ContentForBriefTest : BaseAbstractTest() { } private fun RootPageNode.singleFunctionDescription(className: String): ContentGroup { - val classPage = dfs { it.name == className && (it as ContentPage).documentable is DClass } as ContentPage + val classPage = dfs { it.name == className && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val functionsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Functions } as ContentTable diff --git a/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt b/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt index 832fa6f6..8da3be54 100644 --- a/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt +++ b/plugins/base/src/test/kotlin/content/functions/ContentForConstructors.kt @@ -32,7 +32,7 @@ class ContentForConstructors : BaseAbstractTest() { """.trimIndent(), testConfiguration) { pagesTransformationStage = { module -> val classPage = - module.dfs { it.name == "Example" && (it as ContentPage).documentable is DClass } as ContentPage + module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage val constructorsTable = classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable diff --git a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt index 8c96eab3..59406e1e 100644 --- a/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt +++ b/plugins/base/src/test/kotlin/locationProvider/DokkaLocationProviderTest.kt @@ -53,8 +53,7 @@ class DokkaLocationProviderTest : BaseAbstractTest() { ModulePageNode( name = name, children = packages.pages, - content = stubContentNode, - documentable = null + content = stubContentNode ) ) } @@ -69,7 +68,6 @@ class DokkaLocationProviderTest : BaseAbstractTest() { name = name, children = packages.pages, content = stubContentNode, - documentable = null, dri = emptySet() ) ) @@ -84,7 +82,6 @@ class DokkaLocationProviderTest : BaseAbstractTest() { name = name, children = emptyList(), content = stubContentNode, - documentable = null, dri = emptySet() ) ) diff --git a/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt new file mode 100644 index 00000000..ad3b5d38 --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/MergeImplicitExpectActualDeclarationsTest.kt @@ -0,0 +1,352 @@ +package transformers + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.PluginConfigurationImpl +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest +import org.jetbrains.dokka.model.childrenOfType +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.model.firstChildOfType +import org.jetbrains.dokka.pages.* +import org.jetbrains.kotlin.utils.addIfNotNull +import org.junit.jupiter.api.Test +import utils.assertNotNull +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class MergeImplicitExpectActualDeclarationsTest : BaseAbstractTest() { + + @Suppress("UNUSED_VARIABLE") + private fun configuration(switchOn: Boolean) = dokkaConfiguration { + sourceSets { + val common = sourceSet { + name = "common" + displayName = "common" + analysisPlatform = "common" + sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt") + } + val js = sourceSet { + name = "js" + displayName = "js" + analysisPlatform = "js" + dependentSourceSets = setOf(common.value.sourceSetID) + sourceRoots = listOf("src/jsMain/kotlin/pageMerger/Test.kt") + } + val jvm = sourceSet { + name = "jvm" + displayName = "jvm" + analysisPlatform = "jvm" + sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt") + } + } + pluginsConfigurations.addIfNotNull( + PluginConfigurationImpl( + DokkaBase::class.qualifiedName!!, + DokkaConfiguration.SerializationFormat.JSON, + """{ "mergeImplicitExpectActualDeclarations": $switchOn }""", + ) + ) + } + + private fun ClasslikePageNode.findSectionWithName(name: String) : ContentNode? { + var sectionHeader: ContentHeader? = null + return content.dfs { node -> + node.children.filterIsInstance().any { header -> + header.children.firstOrNull { it is ContentText && it.text == name }?.also { sectionHeader = header } != null + } + }?.children?.dropWhile { child -> child != sectionHeader }?.drop(1)?.firstOrNull() + } + + @Test + fun `should merge fun`() { + testInline( + """ + + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun method1(): String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun method1(): Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val functions = classPage.findSectionWithName("Functions").assertNotNull("Functions") + val method1 = functions.children.singleOrNull().assertNotNull("method1") + + assertEquals( + 2, + method1.firstChildOfType().childrenOfType().size, + "Incorrect number of divergent instances found" + ) + + val methodPage = root.dfs { it.name == "method1" } as? MemberPageNode + assertNotNull(methodPage, "Tested method not found!") + + val divergentGroup = methodPage.content.dfs { it is ContentDivergentGroup } as? ContentDivergentGroup + + assertEquals( + 2, + divergentGroup?.childrenOfType()?.size, + "Incorrect number of divergent instances found in method page" + ) + } + } + } + + @Test + fun `should merge method and prop`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun method1(): String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val props = classPage.findSectionWithName("Properties").assertNotNull("Properties") + val prop1 = props.children.singleOrNull().assertNotNull("prop1") + + val functions = classPage.findSectionWithName("Functions").assertNotNull("Functions") + val method1 = functions.children.singleOrNull().assertNotNull("method1") + } + } + } + + @Test + fun `should merge prop`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val props = classPage.findSectionWithName("Properties").assertNotNull("Properties") + val prop1 = props.children.singleOrNull().assertNotNull("prop1") + + assertEquals( + 2, + prop1.firstChildOfType().inner.children.size, + "Incorrect number of divergent instances found" + ) + + val propPage = root.dfs { it.name == "prop1" } as? MemberPageNode + assertNotNull(propPage, "Tested method not found!") + + val divergentGroup = propPage.content.dfs { it is ContentDivergentGroup } as? ContentDivergentGroup + + assertEquals( + 2, + divergentGroup?.childrenOfType()?.size, + "Incorrect number of divergent instances found in method page" + ) + } + } + } + + @Test + fun `should merge enum and class`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val prop1: String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |enum class classA { + | ENTRY + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val entries = classPage.findSectionWithName("Entries").assertNotNull("Entries") + val entry = entries.children.singleOrNull().assertNotNull("ENTRY") + + val props = classPage.findSectionWithName("Properties").assertNotNull("Properties") + assertEquals( + 3, + props.children.size, + "Incorrect number of properties found in method page" + ) + } + } + } + + fun PageNode.childrenRec(): List = listOf(this) + children.flatMap { it.childrenRec() } + + @Test + fun `should merge enum entries`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |enum class classA { + | SMTH; + | fun method1(): Int + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |enum class classA { + | SMTH; + | fun method1(): Int + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "SMTH" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val functions = classPage.findSectionWithName("Functions").assertNotNull("Functions") + val method1 = functions.children.singleOrNull().assertNotNull("method1") + + assertEquals( + 2, + method1.firstChildOfType().childrenOfType().size, + "Incorrect number of divergent instances found" + ) + } + } + } + + /** + * There is a case when a property and fun from different source sets + * have the same name so pages have the same urls respectively. + */ + @Test + fun `should no merge prop and method with the same name`() { + testInline( + """ + |/src/jvmMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | fun merged():String + |} + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |class classA { + | val merged:String + |} + | + """.trimMargin(), + configuration(true), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val allChildren = root.childrenRec().filterIsInstance() + + assertEquals( + 1, + allChildren.filter { it.name == "merged" }.size, + "Incorrect number of fun pages" + ) + } + } + } + + @Test + fun `should always merge constructor`() { + testInline( + """ + |/src/commonMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |expect class classA(a: Int) + | + |/src/jsMain/kotlin/pageMerger/Test.kt + |package pageMerger + | + |actual class classA(a: Int) + """.trimMargin(), + configuration(false), + cleanupOutput = true + ) { + pagesTransformationStage = { root -> + val classPage = root.dfs { it.name == "classA" } as? ClasslikePageNode + assertNotNull(classPage, "Tested class not found!") + + val constructors = classPage.findSectionWithName("Constructors").assertNotNull("Constructors") + + assertEquals( + 1, + constructors.children.size, + "Incorrect number of constructors" + ) + + val platformHinted = constructors.dfs { it is PlatformHintedContent } as? PlatformHintedContent + + assertEquals( + 2, + platformHinted?.sourceSets?.size, + "Incorrect number of source sets" + ) + } + } + } +} \ No newline at end of file -- cgit