From e9fd8b7bc00491b50e4822acc82e5615ab0bde3b Mon Sep 17 00:00:00 2001 From: "sebastian.sellmair" Date: Fri, 5 Jun 2020 09:07:20 +0200 Subject: Implement `reportUndocumented` option to report undocumented code --- .../ReportUndocumentedTransformerTest.kt | 832 +++++++++++++++++++++ .../DefaultPsiToDocumentableTranslatorTest.kt | 156 ++++ 2 files changed, 988 insertions(+) create mode 100644 plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt create mode 100644 plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt (limited to 'plugins/base/src/test/kotlin') 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..9b6c50da --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt @@ -0,0 +1,832 @@ +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 { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |class X + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("init")) + assertSingleUndocumentedReport(Regex("""sample/X/""")) + } + } + } + + @Test + fun `undocumented non-public class does not get reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |internal class X + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `undocumented function gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X { + | fun x() + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X/x")) + } + } + } + + @Test + fun `undocumented property gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X { + | val x: Int = 0 + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X/x")) + } + } + } + + @Test + fun `undocumented primary constructor does not get reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X(private val x: Int) { + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Disabled + @Test + fun `undocumented secondary constructor gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X { + | constructor(unit: Unit) : this() + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X.*init.*Unit")) + } + } + } + + @Test + fun `undocumented inherited function does not get reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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() + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("B")) + assertSingleUndocumentedReport(Regex("A.*a")) + } + } + } + + @Test + fun `undocumented inherited property does not get reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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() + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("B")) + assertSingleUndocumentedReport(Regex("A.*a")) + } + } + } + + @Test + fun `overridden function does not get reported when super is documented`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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() + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `overridden property does not get reported when super is documented`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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 + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `report disabled by pass configuration`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = false + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |class X + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `report enabled by package configuration`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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 + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + } + } + } + + @Test + fun `report enabled by more specific package configuration`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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 + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("Enabled")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `report disabled by more specific package configuration`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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 + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("Enabled")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `multiplatform undocumented class gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + analysisPlatform = Platform.common.toString() + sourceSetName = "commonMain" + sourceRoots = listOf("src/commonMain/kotlin") + } + + pass { + reportUndocumented = true + analysisPlatform = Platform.jvm.toString() + sourceSetName = "jvmMain" + sourceRoots = listOf("src/jvmMain/kotlin") + dependentSourceRoots = listOf("src/commonMain/kotlin") + dependentSourceSets = listOf("commonMain") + } + } + } + + testInline( + """ + |/src/commonMain/kotlin/sample/Common.kt + |package sample + |expect class X + | + |/src/jvmMain/kotlin/sample/JvmMain.kt + |package sample + |actual class X + """.trimIndent(), + 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 { + passes { + pass { + reportUndocumented = true + analysisPlatform = Platform.common.toString() + sourceSetName = "commonMain" + sourceRoots = listOf("src/commonMain/kotlin") + } + + pass { + reportUndocumented = true + analysisPlatform = Platform.jvm.toString() + sourceSetName = "jvmMain" + sourceRoots = listOf("src/jvmMain/kotlin") + dependentSourceRoots = listOf("src/commonMain/kotlin") + dependentSourceSets = listOf("commonMain") + } + } + } + + testInline( + """ + |/src/commonMain/kotlin/sample/Common.kt + |package sample + |/** Documented */ + |expect class X + | + |/src/jvmMain/kotlin/sample/JvmMain.kt + |package sample + |actual class X + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNumberOfUndocumentedReports(0) + } + } + } + + @Test + fun `java undocumented class gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Test.java + |package sample + |public class Test { } + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("init")) + assertSingleUndocumentedReport(Regex("""Test""")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `java undocumented non-public class does not get reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Test.java + |package sample + |class Test { } + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `java undocumented constructor does not get reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Test.java + |package sample + |/** Documented */ + |public class Test { + | public Test() { + | } + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `java undocumented method gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/X.java + |package sample + |/** Documented */ + |public class X { + | public void x { } + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X.*x")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `java undocumented property gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/X.java + |package sample + |/** Documented */ + |public class X { + | public int x = 0; + |} + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X.*x")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `java undocumented inherited method gets reported`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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() {} + |} + | + """.trimIndent(), + 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 { + passes { + pass { + 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 { + | + |} + | + """.trimIndent(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `java overridden function does not get reported when super is documented`() { + val configuration = dokkaConfiguration { + passes { + pass { + 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() {} + |} + | + """.trimIndent(), + 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 + ) + +} \ 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..5f8a7864 --- /dev/null +++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt @@ -0,0 +1,156 @@ +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 { + passes { + pass { + 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 { + passes { + pass { + 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 { + passes { + pass { + 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\"" + ) + } + } + } + + private fun DModule.documentationOf(className: String, functionName: String): String { + return (packages.single() + .classlikes.single { it.name == className } + .functions.single { it.name == functionName } + .documentation.values.single() + .children.singleOrNull() + .run { this as? Description } + ?.root?.children?.single() as? Text) + ?.body.orEmpty() + } + +} \ No newline at end of file -- cgit