diff options
3 files changed, 204 insertions, 2 deletions
diff --git a/core/src/main/kotlin/model/WithChildren.kt b/core/src/main/kotlin/model/WithChildren.kt index 4aba51c6..59a14acc 100644 --- a/core/src/main/kotlin/model/WithChildren.kt +++ b/core/src/main/kotlin/model/WithChildren.kt @@ -23,6 +23,11 @@ inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfType(): T wher return withDescendants().filterIsInstance<T>().first() } +inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfType( + predicate: (T) -> Boolean +): T where T : WithChildren<*> = withDescendants().filterIsInstance<T>().first(predicate) + + inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfTypeOrNull(): T? where T : WithChildren<*> { return withDescendants().filterIsInstance<T>().firstOrNull() } @@ -66,7 +71,7 @@ fun <T> T.dfs(predicate: (T) -> Boolean): T? where T : WithChildren<T> = if (pre children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() } -fun <T: WithChildren<T>> T.asPrintableTree( +fun <T : WithChildren<T>> T.asPrintableTree( nodeNameBuilder: Appendable.(T) -> Unit = { append(it.toString()) } ): String { fun Appendable.append(element: T, ownPrefix: String, childPrefix: String) { diff --git a/plugins/base/src/test/kotlin/signatures/ObviousTypeSkippingTest.kt b/plugins/base/src/test/kotlin/signatures/ObviousTypeSkippingTest.kt new file mode 100644 index 00000000..e8265667 --- /dev/null +++ b/plugins/base/src/test/kotlin/signatures/ObviousTypeSkippingTest.kt @@ -0,0 +1,195 @@ +package signatures + +import matchers.content.assertNode +import matchers.content.hasExactText +import org.jetbrains.dokka.model.firstMemberOfType +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.testApi.logger.FilteringLogger +import org.jetbrains.dokka.testApi.logger.TestLogger +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource +import kotlin.reflect.KClass + +class ObviousTypeSkippingTest : AbstractCoreTest( + logger = TestLogger(FilteringLogger(minLevel = FilteringLogger.Level.Warn, DokkaConsoleLogger)) +) { + + private fun source(signature: String) = + """ + |/src/test.kt + |package example + | + | $signature + """.trimIndent() + + private val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src") + classpath = listOfNotNull(jvmStdlibPath) + } + } + } + + companion object TestDataSources { + @JvmStatic + fun `run tests for obvious types omitting`() = listOf( + forFunction("fun underTest(): Int = 5", "fun underTest(): Int"), + forFunction("fun underTest() = 5", "fun underTest(): Int"), + forFunction("fun underTest() {}", "fun underTest()"), + forFunction("fun underTest() = println(6)", "fun underTest()"), + forFunction("fun underTest(): Unit = println(6)", "fun underTest()"), + forFunction("fun underTest(): Unit? = if (true) println(6) else null", "fun underTest(): Unit?"), + forFunction("fun underTest() = if (true) println(6) else null", "fun underTest(): Unit?"), + forFunction("fun underTest(): Any = if (true) 7 else true", "fun underTest(): Any"), + forFunction("fun underTest() = if (true) 7 else true", "fun underTest(): Any"), + forFunction("fun underTest(): Any? = if (true) 7 else (null as String?)", "fun underTest(): Any?"), + forFunction("fun underTest() = if (true) 7 else (null as String?)", "fun underTest(): Any?"), + forFunction("fun underTest(arg: Int) {}", "fun underTest(arg: Int)"), + forFunction("fun underTest(arg: Unit) {}", "fun underTest(arg: Unit)"), + forFunction("fun <T: Iterable<Any>> underTest(arg: T) {}", "fun <T : Iterable<Any>> underTest(arg: T)"), + forFunction("fun <T: Iterable<Any?>> underTest(arg: T) {}", "fun <T : Iterable<Any?>> underTest(arg: T)"), + forFunction("fun <T> underTest(arg: T) {}", "fun <T> underTest(arg: T)"), + forFunction("fun <T: Any> underTest(arg: T) {}", "fun <T : Any> underTest(arg: T)"), + forFunction("fun <T: Any?> underTest(arg: T) {}", "fun <T> underTest(arg: T)"), + forProperty("val underTest: Int = 5", "val underTest: Int"), + forProperty("val underTest = 5", "val underTest: Int"), + forProperty("val underTest: Unit = println(5)", "val underTest: Unit"), + forProperty("val underTest = println(5)", "val underTest: Unit"), + forProperty("val underTest: Unit? = if (true) println(5) else null", "val underTest: Unit?"), + forProperty("val underTest = if (true) println(5) else null", "val underTest: Unit?"), + forProperty("val underTest: Any = if (true) println(5) else 5", "val underTest: Any"), + forProperty("val underTest = if (true) println(5) else 5", "val underTest: Any"), + forFunction("fun <T: Iterable<Any>> T.underTest() {}", "fun <T : Iterable<Any>> T.underTest()"), + forFunction("fun <T: Iterable<Any?>> T.underTest() {}", "fun <T : Iterable<Any?>> T.underTest()"), + forFunction("fun <T: Iterable<Any?>?> T.underTest() {}", "fun <T : Iterable<Any?>?> T.underTest()"), + forFunction("fun <T: Any> T.underTest() {}", "fun <T : Any> T.underTest()"), + forFunction("fun <T: Any?> T.underTest() {}", "fun <T> T.underTest()"), + forFunction("fun <T> T.underTest() {}", "fun <T> T.underTest()"), + forClass("class Testable<T: Any>", "class Testable<T : Any>"), + forClass("class Testable<T: Any?>", "class Testable<T>"), + forClass("class Testable<T: Any?>(t: T)", "class Testable<T>(t: T)"), + forClass("class Testable<T>", "class Testable<T>"), + forClass("class Testable(butWhy: Unit)", "class Testable(butWhy: Unit)"), + forMethod("class Testable { fun underTest(): Int = 5 }", "fun underTest(): Int"), + forMethod("class Testable { fun underTest() = 5 }", "fun underTest(): Int"), + forMethod("class Testable { fun underTest() {} }", "fun underTest()"), + forMethod("class Testable { fun underTest() = println(6) }", "fun underTest()"), + forMethod("class Testable { fun underTest(): Unit = println(6) }", "fun underTest()"), + forMethod( + "class Testable { fun underTest(): Unit? = if (true) println(6) else null }", + "fun underTest(): Unit?" + ), + forClassProperty("class Testable { val underTest: Unit = println(5) }", "val underTest: Unit"), + forClassProperty("class Testable { val underTest = println(5) }", "val underTest: Unit"), + forClassProperty( + "class Testable { val underTest: Unit? = if (true) println(5) else null }", + "val underTest: Unit?" + ), + forClassProperty( + "class Testable { val underTest = if (true) println(5) else null }", + "val underTest: Unit?" + ), + forClassProperty( + "class Testable { val underTest: Any = if (true) println(5) else 5 }", + "val underTest: Any" + ), + forClassProperty("class Testable { val underTest = if (true) println(5) else 5 }", "val underTest: Any"), + ) + } + + @ParameterizedTest(name = "{0}") + @MethodSource + fun `run tests for obvious types omitting`(testData: TestData) { + val (codeFragment, expectedSignature, placesToTest) = testData + testInline( + query = source(codeFragment), + configuration = configuration + ) { + pagesTransformationStage = { root -> + placesToTest.forEach { place -> + try { + when (place) { + is OnOwnPage -> + root.firstMemberOfType<ContentPage> { it.name == place.name }.content + .firstMemberOfType<ContentGroup> { it.dci.kind == ContentKind.Symbol } + .assertNode { hasExactText(expectedSignature) } + is OnParentPage -> + root.firstMemberOfType<ContentPage> { + place.pageType.isInstance(it) && (place.parentName.isNullOrBlank() || place.parentName == it.name) + } + .content + .firstMemberOfType<ContentGroup> { + it.dci.kind == place.section && (place.selfName.isNullOrBlank() || + it.dci.dri.toString().contains(place.selfName)) + } + .firstMemberOfType<ContentGroup> { it.dci.kind == ContentKind.Symbol } + .assertNode { hasExactText(expectedSignature) } + } + } catch (e: Throwable) { + logger.warn("$testData") // Because gradle has serious problem rendering custom test names + throw e + } + } + } + } + } + +} + +sealed class Place +data class OnOwnPage(val name: String) : Place() +data class OnParentPage( + val pageType: KClass<out ContentPage>, + val section: Kind, + val parentName: String? = null, + val selfName: String? = null +) : Place() + +data class TestData( + val codeFragment: String, + val expectedSignature: String, + val placesToTest: Iterable<Place> +) { + constructor(codeFragment: String, expectedSignature: String, vararg placesToTest: Place) + : this(codeFragment, expectedSignature, placesToTest.asIterable()) + + override fun toString() = "[code = \"$codeFragment\"]" +} + +private fun forFunction(codeFragment: String, expectedSignature: String, functionName: String = "underTest") = + TestData( + codeFragment, + expectedSignature, + OnParentPage(PackagePageNode::class, ContentKind.Functions), + OnOwnPage(functionName) + ) + +private fun forMethod( + codeFragment: String, + expectedSignature: String, + functionName: String = "underTest", + className: String = "Testable" +) = + TestData( + codeFragment, + expectedSignature, + OnParentPage(ClasslikePageNode::class, ContentKind.Functions, className, functionName), + OnOwnPage(functionName) + ) + +private fun forProperty(codeFragment: String, expectedSignature: String) = + TestData(codeFragment, expectedSignature, OnParentPage(PackagePageNode::class, ContentKind.Properties)) + +private fun forClassProperty(codeFragment: String, expectedSignature: String, className: String = "Testable") = + TestData(codeFragment, expectedSignature, OnParentPage(ClasslikePageNode::class, ContentKind.Properties, className)) + +private fun forClass(codeFragment: String, expectedSignature: String, className: String = "Testable") = + TestData( + codeFragment, + expectedSignature, + OnParentPage(PackagePageNode::class, ContentKind.Classlikes), + OnOwnPage(className) + )
\ No newline at end of file diff --git a/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt b/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt index e1026a97..67c0e692 100644 --- a/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt +++ b/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt @@ -30,12 +30,14 @@ class ContentMatcherBuilder<T : ContentComposite> @PublishedApi internal constru internal val children = mutableListOf<MatcherElement>() internal val assertions = mutableListOf<T.() -> Unit>() - fun build() = CompositeMatcher(kclass, children) { assertions.forEach { it() } } + fun build() = CompositeMatcher(kclass, childrenOrSkip()) { assertions.forEach { it() } } // part of DSL that cannot be defined as an extension operator fun String.unaryPlus() { children += TextMatcher(this) } + + private fun childrenOrSkip() = if (children.isEmpty() && assertions.isNotEmpty()) listOf(Anything) else children } fun <T : ContentComposite> ContentMatcherBuilder<T>.check(assertion: T.() -> Unit) { |