diff options
Diffstat (limited to 'plugins/base/src')
-rw-r--r-- | plugins/base/src/test/kotlin/issues/IssuesTest.kt | 67 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/model/ClassesTest.kt | 448 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/model/CommentTest.kt | 332 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/model/FunctionsTest.kt | 315 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/model/JavaTest.kt | 367 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/model/PackagesTest.kt | 126 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/model/PropertyTest.kt | 176 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/utils/ModelUtils.kt | 33 | ||||
-rw-r--r-- | plugins/base/src/test/kotlin/utils/TestUtils.kt | 68 |
9 files changed, 1932 insertions, 0 deletions
diff --git a/plugins/base/src/test/kotlin/issues/IssuesTest.kt b/plugins/base/src/test/kotlin/issues/IssuesTest.kt new file mode 100644 index 00000000..ea2f0f8e --- /dev/null +++ b/plugins/base/src/test/kotlin/issues/IssuesTest.kt @@ -0,0 +1,67 @@ +package issues + +import org.jetbrains.dokka.model.Class +import org.jetbrains.dokka.model.Function +import org.junit.Test +import utils.AbstractModelTest + +class IssuesTest : AbstractModelTest("/src/main/kotlin/issues/Test.kt", "issues") { + + @Test + fun errorClasses() { + inlineModelTest( + """ + |class Test(var value: String) { + | fun test(): List<String> = emptyList() + | fun brokenApply(v: String) = apply { value = v } + | + | fun brokenRun(v: String) = run { + | value = v + | this + | } + | + | fun brokenLet(v: String) = let { + | it.value = v + | it + | } + | + | fun brokenGenerics() = listOf("a", "b", "c") + | + | fun working(v: String) = doSomething() + | + | fun doSomething(): String = "Hello" + |} + """ + ) { + with((this / "issues" / "Test").cast<Class>()) { + // passes + (this / "working").cast<Function>().type.constructorFqName equals "kotlin.String" + (this / "doSomething").cast<Function>().type.constructorFqName equals "kotlin.String" + + // fails + (this / "brokenGenerics").cast<Function>().type.constructorFqName equals "kotlin.collections.List" + (this / "brokenApply").cast<Function>().type.constructorFqName equals "issues.Test" + (this / "brokenRun").cast<Function>().type.constructorFqName equals "issues.Test" + (this / "brokenLet").cast<Function>().type.constructorFqName equals "issues.Test" + } + } + } + + //@Test + // fun errorClasses() { + // checkSourceExistsAndVerifyModel("testdata/issues/errorClasses.kt", + // modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true, withKotlinRuntime = true)) { model -> + // val cls = model.members.single().members.single() + // + // fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name + // assertEquals("Test", cls.members[1].returnType()) + // assertEquals("Test", cls.members[2].returnType()) + // assertEquals("Test", cls.members[3].returnType()) + // assertEquals("List", cls.members[4].returnType()) + // assertEquals("String", cls.members[5].returnType()) + // assertEquals("String", cls.members[6].returnType()) + // assertEquals("String", cls.members[7].returnType()) + // } + // } + +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt new file mode 100644 index 00000000..2332df61 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt @@ -0,0 +1,448 @@ +package model + +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.model.Object +import org.jetbrains.dokka.model.WithAbstraction.Modifier +import org.jetbrains.kotlin.descriptors.Visibilities +import org.junit.Test +import utils.AbstractModelTest +import utils.assertNotNull +import utils.supers + + +class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") { + + @Test + fun emptyClass() { + inlineModelTest( + """ + |class Klass {}""" + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + children counts 4 + } + } + } + + @Test + fun emptyObject() { + inlineModelTest( + """ + |object Obj {} + """ + ) { + with((this / "classes" / "Obj").cast<Object>()) { + name equals "Obj" + children counts 3 + } + } + } + + @Test + fun classWithConstructor() { + inlineModelTest( + """ + |class Klass(name: String) + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + children counts 4 + + with(constructors.firstOrNull().assertNotNull("Constructor")) { + visibility.values allEquals Visibilities.PUBLIC + parameters counts 1 + with(parameters.firstOrNull().assertNotNull("Constructor parameter")) { + name equals "name" + type.constructorFqName equals "kotlin.String" + } + } + + } + } + } + + @Test + fun classWithFunction() { + inlineModelTest( + """ + |class Klass { + | fun fn() {} + |} + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + children counts 5 + + with((this / "fn").cast<Function>()) { + type.constructorFqName equals "kotlin.Unit" + parameters counts 0 + visibility.values allEquals Visibilities.PUBLIC + } + } + } + } + + @Test + fun classWithProperty() { + inlineModelTest( + """ + |class Klass { + | val name: String = "" + |} + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + children counts 5 + + with((this / "name").cast<Property>()) { + name equals "name" + // TODO property name + } + } + } + } + + @Test + fun classWithCompanionObject() { + inlineModelTest( + """ + |class Klass() { + | companion object { + | val x = 1 + | fun foo() {} + | } + |} + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + children counts 5 + + with((this / "Companion").cast<Object>()) { + name equals "Companion" + children counts 5 + + with((this / "x").cast<Property>()) { + name equals "x" + } + + with((this / "foo").cast<Function>()) { + name equals "foo" + parameters counts 0 + type.constructorFqName equals "kotlin.Unit" + } + } + } + } + } + + @Test + fun dataClass() { + inlineModelTest( + """ + |data class Klass() {} + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + visibility.values allEquals Visibilities.PUBLIC + // TODO data modifier + } + } + } + +// @Test fun dataClass() { +// verifyPackageMember("testdata/classes/dataClass.kt", defaultModelConfig) { cls -> +// val modifiers = cls.details(NodeKind.Modifier).map { it.name } +// assertTrue("data" in modifiers) +// } +// } + + @Test + fun sealedClass() { + inlineModelTest( + """ + |sealed class Klass() {} + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + modifier equals WithAbstraction.Modifier.Sealed + } + } + } + +// // TODO modifiers +// @Test fun annotatedClassWithAnnotationParameters() { +// checkSourceExistsAndVerifyModel( +// "testdata/classes/annotatedClassWithAnnotationParameters.kt", +// defaultModelConfig +// ) { model -> +// with(model.members.single().members.single()) { +// with(deprecation!!) { +// assertEquals("Deprecated", name) +// assertEquals(Content.Empty, content) +// assertEquals(NodeKind.Annotation, kind) +// assertEquals(1, details.count()) +// with(details[0]) { +// assertEquals(NodeKind.Parameter, kind) +// assertEquals(1, details.count()) +// with(details[0]) { +// assertEquals(NodeKind.Value, kind) +// assertEquals("\"should no longer be used\"", name) +// } +// } +// } +// } +// } +// } + + @Test + fun notOpenClass() { + inlineModelTest( + """ + |open class C() { + | open fun f() {} + |} + | + |class D() : C() { + | override fun f() {} + |} + """ + ) { + val C = (this / "classes" / "C").cast<Class>() + val D = (this / "classes" / "D").cast<Class>() + + with(C) { + modifier equals Modifier.Open + with((this / "f").cast<Function>()) { + modifier equals Modifier.Open + } + } + with(D) { + modifier equals Modifier.Final + with((this / "f").cast<Function>()) { + modifier equals Modifier.Open + } + D.supertypes.flatMap { it.component2() }.firstOrNull() equals C.dri + } + } + } + + @Test + fun indirectOverride() { + inlineModelTest( + """ + |abstract class C() { + | abstract fun foo() + |} + | + |abstract class D(): C() + | + |class E(): D() { + | override fun foo() {} + |} + """ + ) { + val C = (this / "classes" / "C").cast<Class>() + val D = (this / "classes" / "D").cast<Class>() + val E = (this / "classes" / "E").cast<Class>() + + with(C) { + modifier equals Modifier.Abstract + ((this / "foo").cast<Function>()).modifier equals Modifier.Abstract + } + + with(D) { + modifier equals Modifier.Abstract + } + + with(E) { + modifier equals Modifier.Final + + } + D.supers.firstOrNull() equals C.dri + E.supers.firstOrNull() equals D.dri + } + } + + @Test // todo inner class + fun innerClass() { + inlineModelTest( + """ + |class C { + | inner class D {} + |} + """ + ) { + with((this / "classes" / "C").cast<Class>()) { + + with((this / "D").cast<Class>()) { + } + } + } + } + +// // TODO modifiers +// @Test fun innerClass() { +// verifyPackageMember("testdata/classes/innerClass.kt", defaultModelConfig) { cls -> +// val innerClass = cls.members.single { it.name == "D" } +// val modifiers = innerClass.details(NodeKind.Modifier) +// assertEquals(3, modifiers.size) +// assertEquals("inner", modifiers[2].name) +// } +// } + + @Test + fun companionObjectExtension() { + inlineModelTest( + """ + |class Klass { + | companion object Default {} + |} + | + |/** + | * The def + | */ + |val Klass.Default.x: Int get() = 1 + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + + with((this / "Default").cast<Object>()) { + name equals "Default" + // TODO extensions + } + } + } + } + +// @Test fun companionObjectExtension() { +// checkSourceExistsAndVerifyModel("testdata/classes/companionObjectExtension.kt", defaultModelConfig) { model -> +// val pkg = model.members.single() +// val cls = pkg.members.single { it.name == "Foo" } +// val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty } +// assertEquals(1, extensions.size) +// } +// } + + @Test + fun secondaryConstructor() { + inlineModelTest( + """ + |class C() { + | /** This is a secondary constructor. */ + | constructor(s: String): this() {} + |} + """ + ) { + with((this / "classes" / "C").cast<Class>()) { + name equals "C" + constructors counts 2 + + constructors.map { it.name } allEquals "<init>" + + with(constructors.find { it.parameters.isNullOrEmpty() } notNull "C()") { + parameters counts 0 + } + + with(constructors.find { it.parameters.isNotEmpty() } notNull "C(String)") { + parameters counts 1 + with(parameters.firstOrNull() notNull "Constructor parameter") { + name equals "s" + type.constructorFqName equals "kotlin.String" + } + } + } + } + } + + // TODO modifiers +// @Test fun sinceKotlin() { +// checkSourceExistsAndVerifyModel("testdata/classes/sinceKotlin.kt", defaultModelConfig) { model -> +// with(model.members.single().members.single()) { +// assertEquals("1.1", sinceKotlin) +// } +// } +// } + + @Test + fun privateCompanionObject() { + inlineModelTest( + """ + |class Klass { + | private companion object { + | fun fn() {} + | val a = 0 + | } + |} + """ + ) { + with((this / "classes" / "Klass").cast<Class>()) { + name equals "Klass" + + with((this / "Companion").cast<Object>()) { + name equals "Companion" + visibility.values allEquals Visibilities.PRIVATE + + with((this / "fn").cast<Function>()) { + name equals "fn" + parameters counts 0 + receiver equals null + } + } + } + } + } + + // TODO annotations +// @Test +// fun annotatedClass() { +// verifyPackageMember("testdata/classes/annotatedClass.kt", ModelConfig( +// analysisPlatform = analysisPlatform, +// withKotlinRuntime = true +// ) +// ) { cls -> +// Assert.assertEquals(1, cls.annotations.count()) +// with(cls.annotations[0]) { +// Assert.assertEquals("Strictfp", name) +// Assert.assertEquals(Content.Empty, content) +// Assert.assertEquals(NodeKind.Annotation, kind) +// } +// } +// } + + +// TODO annotations + +// @Test fun javaAnnotationClass() { +// checkSourceExistsAndVerifyModel( +// "testdata/classes/javaAnnotationClass.kt", +// modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true) +// ) { model -> +// with(model.members.single().members.single()) { +// Assert.assertEquals(1, annotations.count()) +// with(annotations[0]) { +// Assert.assertEquals("Retention", name) +// Assert.assertEquals(Content.Empty, content) +// Assert.assertEquals(NodeKind.Annotation, kind) +// with(details[0]) { +// Assert.assertEquals(NodeKind.Parameter, kind) +// Assert.assertEquals(1, details.count()) +// with(details[0]) { +// Assert.assertEquals(NodeKind.Value, kind) +// Assert.assertEquals("RetentionPolicy.SOURCE", name) +// } +// } +// } +// } +// } +// } + +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/CommentTest.kt b/plugins/base/src/test/kotlin/model/CommentTest.kt new file mode 100644 index 00000000..d576cf49 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/CommentTest.kt @@ -0,0 +1,332 @@ +package model + +import org.jetbrains.dokka.model.Property +import org.jetbrains.dokka.model.doc.CustomWrapperTag +import org.jetbrains.dokka.model.doc.Text +import org.junit.Test +import utils.* + +class CommentTest : AbstractModelTest("/src/main/kotlin/comment/Test.kt", "comment") { + + @Test + fun codeBlockComment() { + inlineModelTest( + """ + |/** + | * ```brainfuck + | * ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. + | * ``` + | */ + |val prop1 = "" + | + | + |/** + | * ``` + | * a + b - c + | * ``` + | */ + |val prop2 = "" + """ + ) { + with((this / "comment" / "prop1").cast<Property>()) { + name equals "prop1" + with(this.docs().firstOrNull()?.root.assertNotNull("Code")) { + (children.firstOrNull() as? Text) + ?.body equals "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." + + params["lang"] equals "brainfuck" + } + } + with((this / "comment" / "prop2").cast<Property>()) { + name equals "prop2" + comments() equals "a + b - c" + } + } + } + + @Test + fun emptyDoc() { + inlineModelTest( + """ + val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + name equals "property" + comments() equals "" + } + } + } + + @Test + fun emptyDocButComment() { + inlineModelTest( + """ + |/* comment */ + |val property = "test" + |fun tst() = property + """ + ) { + val p = this + with((this / "comment" / "property").cast<Property>()) { + comments() equals "" + } + } + } + + @Test + fun multilineDoc() { + inlineModelTest( + """ + |/** + | * doc1 + | * + | * doc2 + | * doc3 + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "doc1\ndoc2 doc3" + } + } + } + + @Test + fun multilineDocWithComment() { + inlineModelTest( + """ + |/** + | * doc1 + | * + | * doc2 + | * doc3 + | */ + |// comment + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "doc1\ndoc2 doc3" + } + } + } + + @Test + fun oneLineDoc() { + inlineModelTest( + """ + |/** doc */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "doc" + } + } + } + + @Test + fun oneLineDocWithComment() { + inlineModelTest( + """ + |/** doc */ + |// comment + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "doc" + } + } + } + + @Test + fun oneLineDocWithEmptyLine() { + inlineModelTest( + """ + |/** doc */ + | + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "doc" + } + } + } + + @Test + fun emptySection() { + inlineModelTest( + """ + |/** + | * Summary + | * @one + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "Summary\none: []" + docs().find { it is CustomWrapperTag && it.name == "one" }.let { + with(it.assertNotNull("'one' entry")) { + root.children counts 0 + root.params.keys counts 0 + } + } + } + } + } + + @Test + fun quotes() { + inlineModelTest( + """ + |/** it's "useful" */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals """it's "useful"""" + } + } + } + + @Test + fun section1() { + inlineModelTest( + """ + |/** + | * Summary + | * @one section one + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "Summary\none: [section one]" + } + } + } + + + @Test + fun section2() { + inlineModelTest( + """ + |/** + | * Summary + | * @one section one + | * @two section two + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "Summary\none: [section one]\ntwo: [section two]" + } + } + } + + @Test + fun multilineSection() { + inlineModelTest( + """ + |/** + | * Summary + | * @one + | * line one + | * line two + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + comments() equals "Summary\none: [line one line two]" + } + } + } + +// @Test todo + fun directive() { + inlineModelTest( + """ + |/** + | * Summary + | * + | * @sample example1 + | * @sample example2 + | * @sample X.example3 + | * @sample X.Y.example4 + | */ + |val property = "test" + | + |fun example1(node: String) = if (true) { + | println(property) + |} + | + |fun example2(node: String) { + | if (true) { + | println(property) + | } + |} + | + |class X { + | fun example3(node: String) { + | if (true) { + | println(property) + | } + | } + | + | class Y { + | fun example4(node: String) { + | if (true) { + | println(property) + | } + | } + | } + |} + """ + ) { + with((this / "comment" / "property").cast<Property>()) { + this + } + } + } + + +// @Test fun directive() { +// checkSourceExistsAndVerifyModel("testdata/comments/directive.kt", defaultModelConfig) { model -> +// with(model.members.single().members.first()) { +// assertEquals("Summary", content.summary.toTestString()) +// with (content.description) { +// assertEqualsIgnoringSeparators(""" +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |""".trimMargin(), toTestString()) +// } +// } +// } +// } + +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/FunctionsTest.kt b/plugins/base/src/test/kotlin/model/FunctionsTest.kt new file mode 100644 index 00000000..9554ad02 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/FunctionsTest.kt @@ -0,0 +1,315 @@ +package model + +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.model.Package +import org.junit.Test +import utils.* + +class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "function") { + + @Test + fun function() { + inlineModelTest( + """ + |/** + | * Function fn + | */ + |fun fn() {} + """ + ) { + with((this / "function" / "fn").cast<Function>()) { + name equals "fn" + type.constructorFqName equals "kotlin.Unit" + this.children.assertCount(0, "Function children: ") + } + } + } + + @Test + fun overloads() { + inlineModelTest( + """ + |/** + | * Function fn + | */ + |fun fn() {} + | /** + | * Function fn(Int) + | */ + |fun fn(i: Int) {} + """ + ) { + with((this / "function").cast<Package>()) { + val fn1 = functions.find { + it.name == "fn" && it.parameters.isNullOrEmpty() + }.assertNotNull("fn()") + val fn2 = functions.find { + it.name == "fn" && it.parameters.isNotEmpty() + }.assertNotNull("fn(Int)") + + with(fn1) { + name equals "fn" + parameters.assertCount(0) + } + + with(fn2) { + name equals "fn" + parameters.assertCount(1) + parameters.first().type.constructorFqName equals "kotlin.Int" + } + } + } + } + + @Test + fun functionWithReceiver() { + inlineModelTest( + """ + |/** + | * Function with receiver + | */ + |fun String.fn() {} + | + |/** + | * Function with receiver + | */ + |fun String.fn(x: Int) {} + """ + ) { + with((this / "function").cast<Package>()) { + val fn1 = functions.find { + it.name == "fn" && it.parameters.isNullOrEmpty() + }.assertNotNull("fn()") + val fn2 = functions.find { + it.name == "fn" && it.parameters.count() == 1 + }.assertNotNull("fn(Int)") + + with(fn1) { + name equals "fn" + parameters counts 0 + receiver.assertNotNull("fn() receiver") + } + + with(fn2) { + name equals "fn" + parameters counts 1 + receiver.assertNotNull("fn(Int) receiver") + parameters.first().type.constructorFqName equals "kotlin.Int" + } + } + } + } + + @Test + fun functionWithParams() { + inlineModelTest( + """ + |/** + | * Multiline + | * + | * Function + | * Documentation + | */ + |fun function(/** parameter */ x: Int) { + |} + """ + ) { + with((this / "function" / "function").cast<Function>()) { + comments() equals "Multiline\nFunction Documentation" + + name equals "function" + parameters counts 1 + parameters.firstOrNull().assertNotNull("Parameter: ").also { + it.name equals "x" + it.type.constructorFqName equals "kotlin.Int" + it.comments() equals "parameter" + } + + type.assertNotNull("Return type: ").constructorFqName equals "kotlin.Unit" + } + } + } + +// TODO add modifiers - start + + @Test + fun functionWithNotDocumentedAnnotation() { + inlineModelTest( + """ + |@Suppress("FOO") fun f() {} + """ + ) { + // TODO add annotations + + with((this / "function" / "f").cast<Function>()) { + assert(false) { "No annotation data" } + } + } + } + +// @Test fun functionWithNotDocumentedAnnotation() { +// verifyPackageMember("testdata/functions/functionWithNotDocumentedAnnotation.kt", defaultModelConfig) { func -> +// assertEquals(0, func.annotations.count()) +// } +// } + + @Test + fun inlineFunction() { + inlineModelTest( + """ + |inline fun f(a: () -> String) {} + """ + ) { + // TODO add data about inline + + with((this / "function" / "f").cast<Function>()) { + assert(false) { "No inline data" } + } + } + } + +// @Test fun inlineFunction() { +// verifyPackageMember("testdata/functions/inlineFunction.kt", defaultModelConfig) { func -> +// val modifiers = func.details(NodeKind.Modifier).map { it.name } +// assertTrue("inline" in modifiers) +// } +// } + + @Test + fun suspendFunction() { + inlineModelTest( + """ + |suspend fun f() {} + """ + ) { + // TODO add data about suspend + + with((this / "function" / "f").cast<Function>()) { + assert(false) { "No suspend data" } + } + } + } + +// @Test fun suspendFunction() { +// verifyPackageMember("testdata/functions/suspendFunction.kt") { func -> +// val modifiers = func.details(NodeKind.Modifier).map { it.name } +// assertTrue("suspend" in modifiers) +// } +// } + +// @Test fun suspendInlineFunctionOrder() { +// verifyPackageMember("testdata/functions/suspendInlineFunction.kt") { func -> +// val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter { +// it == "suspend" || it == "inline" +// } +// +// assertEquals(listOf("suspend", "inline"), modifiers) +// } +// } +// +// @Test fun inlineSuspendFunctionOrderChanged() { +// verifyPackageMember("testdata/functions/inlineSuspendFunction.kt") { func -> +// val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter { +// it == "suspend" || it == "inline" +// } +// +// assertEquals(listOf("suspend", "inline"), modifiers) +// } +// } +// +// @Test fun functionWithAnnotatedParam() { +// checkSourceExistsAndVerifyModel("testdata/functions/functionWithAnnotatedParam.kt", defaultModelConfig) { model -> +// with(model.members.single().members.single { it.name == "function" }) { +// with(details(NodeKind.Parameter).first()) { +// assertEquals(1, annotations.count()) +// with(annotations[0]) { +// assertEquals("Fancy", name) +// assertEquals(Content.Empty, content) +// assertEquals(NodeKind.Annotation, kind) +// } +// } +// } +// } +// } +// +// @Test fun functionWithNoinlineParam() { +// verifyPackageMember("testdata/functions/functionWithNoinlineParam.kt", defaultModelConfig) { func -> +// with(func.details(NodeKind.Parameter).first()) { +// val modifiers = details(NodeKind.Modifier).map { it.name } +// assertTrue("noinline" in modifiers) +// } +// } +// } +// +// @Test fun annotatedFunctionWithAnnotationParameters() { +// checkSourceExistsAndVerifyModel( +// "testdata/functions/annotatedFunctionWithAnnotationParameters.kt", +// defaultModelConfig +// ) { model -> +// with(model.members.single().members.single { it.name == "f" }) { +// assertEquals(1, annotations.count()) +// with(annotations[0]) { +// assertEquals("Fancy", name) +// assertEquals(Content.Empty, content) +// assertEquals(NodeKind.Annotation, kind) +// assertEquals(1, details.count()) +// with(details[0]) { +// assertEquals(NodeKind.Parameter, kind) +// assertEquals(1, details.count()) +// with(details[0]) { +// assertEquals(NodeKind.Value, kind) +// assertEquals("1", name) +// } +// } +// } +// } +// } +// } + +// TODO add modifiers - end + +// @Test +// fun functionWithDefaultParameter() { +// inlineModelTest( +// """ +// |/src/main/kotlin/function/Test.kt +// |package function +// |fun f(x: String = "") {} +// """ +// ) { +// // TODO add default value data +// +// with(this / "function" / "f" cast Function::class) { +// parameters.forEach { p -> +// p.name equals "x" +// p.type.constructorFqName.assertNotNull("Parameter type: ") equals "kotlin.String" +// assert(false) { "Add default value data" } +// } +// } +// } +// } + +// @Test fun functionWithDefaultParameter() { +// checkSourceExistsAndVerifyModel("testdata/functions/functionWithDefaultParameter.kt", defaultModelConfig) { model -> +// with(model.members.single().members.single()) { +// with(details.elementAt(3)) { +// val value = details(NodeKind.Value) +// assertEquals(1, value.count()) +// with(value[0]) { +// assertEquals("\"\"", name) +// } +// } +// } +// } +// } +// +// @Test fun sinceKotlin() { +// checkSourceExistsAndVerifyModel("testdata/functions/sinceKotlin.kt", defaultModelConfig) { model -> +// with(model.members.single().members.single()) { +// assertEquals("1.1", sinceKotlin) +// } +// } +// } +//} + +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt new file mode 100644 index 00000000..ea454763 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/JavaTest.kt @@ -0,0 +1,367 @@ +package model + +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Enum +import org.jetbrains.dokka.model.Function +import org.junit.Assert.assertTrue +import org.junit.Test +import utils.AbstractModelTest +import utils.assertNotNull + +class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { + + @Test //todo params in comments + fun function() { + inlineModelTest( + """ + |class Test { + | /** + | * Summary for Function + | * @param name is String parameter + | * @param value is int parameter + | */ + | public void fn(String name, int value) {} + |} + """ + ) { + with((this / "java" / "Test").cast<Class>()) { + name equals "Test" + children counts 1 + with((this / "fn").cast<Function>()) { + name equals "fn" + this + } + } + } + } + + //@Test fun function() { + // verifyJavaPackageMember("testdata/java/member.java", defaultModelConfig) { cls -> + // assertEquals("Test", cls.name) + // assertEquals(NodeKind.Class, cls.kind) + // with(cls.members(NodeKind.Function).single()) { + // assertEquals("fn", name) + // assertEquals("Summary for Function", content.summary.toTestString().trimEnd()) + // assertEquals(3, content.sections.size) + // with(content.sections[0]) { + // assertEquals("Parameters", tag) + // assertEquals("name", subjectName) + // assertEquals("render(Type:String,SUMMARY): is String parameter", toTestString()) + // } + // with(content.sections[1]) { + // assertEquals("Parameters", tag) + // assertEquals("value", subjectName) + // assertEquals("render(Type:Int,SUMMARY): is int parameter", toTestString()) + // } + // assertEquals("Unit", detail(NodeKind.Type).name) + // assertTrue(members.none()) + // assertTrue(links.none()) + // with(details.first { it.name == "name" }) { + // assertEquals(NodeKind.Parameter, kind) + // assertEquals("String", detail(NodeKind.Type).name) + // } + // with(details.first { it.name == "value" }) { + // assertEquals(NodeKind.Parameter, kind) + // assertEquals("Int", detail(NodeKind.Type).name) + // } + // } + // } + // } + + @Test // todo + fun memberWithModifiers() { + inlineModelTest( + """ + |class Test { + | /** + | * Summary for Function + | * @param name is String parameter + | * @param value is int parameter + | */ + | public void fn(String name, int value) {} + |} + """ + ) { + with((this / "java" / "Test" / "fn").cast<Function>()) { + this + } + } + } + + // @Test fun memberWithModifiers() { + // verifyJavaPackageMember("testdata/java/memberWithModifiers.java", defaultModelConfig) { cls -> + // val modifiers = cls.details(NodeKind.Modifier).map { it.name } + // assertTrue("abstract" in modifiers) + // with(cls.members.single { it.name == "fn" }) { + // assertEquals("protected", details[0].name) + // } + // with(cls.members.single { it.name == "openFn" }) { + // assertEquals("open", details[1].name) + // } + // } + // } + + @Test + fun superClass() { + inlineModelTest( + """ + |public class Foo extends Exception implements Cloneable {} + """ + ) { + with((this / "java" / "Foo").cast<Class>()) { + val sups = listOf("Exception", "Cloneable") + assertTrue( + "Foo must extend ${sups.joinToString(", ")}", + sups.all { s -> supertypes.map.values.flatten().any { it.classNames == s } }) + } + } + } + + @Test + fun arrayType() { + inlineModelTest( + """ + |class Test { + | public String[] arrayToString(int[] data) { + | return null; + | } + |} + """ + ) { + with((this / "java" / "Test").cast<Class>()) { + name equals "Test" + children counts 1 + + with((this / "arrayToString").cast<Function>()) { + name equals "arrayToString" + type.constructorFqName equals "java.lang.String[]" + with(parameters.firstOrNull().assertNotNull("parameters")) { + name equals "data" + type.constructorFqName equals "int[]" + } + } + } + } + } + + @Test + fun typeParameter() { + inlineModelTest( + """ + |class Foo<T extends Comparable<T>> { + | public <E> E foo(); + |} + """ + ) { + with((this / "java" / "Foo").cast<Class>()) { + this + } + } + } + + // @Test fun typeParameter() { + // verifyJavaPackageMember("testdata/java/typeParameter.java", defaultModelConfig) { cls -> + // val typeParameters = cls.details(NodeKind.TypeParameter) + // with(typeParameters.single()) { + // assertEquals("T", name) + // with(detail(NodeKind.UpperBound)) { + // assertEquals("Comparable", name) + // assertEquals("T", detail(NodeKind.Type).name) + // } + // } + // with(cls.members(NodeKind.Function).single()) { + // val methodTypeParameters = details(NodeKind.TypeParameter) + // with(methodTypeParameters.single()) { + // assertEquals("E", name) + // } + // } + // } + // } + + @Test + fun constructors() { + inlineModelTest( + """ + |class Test { + | public Test() {} + | + | public Test(String s) {} + |} + """ + ) { + with((this / "java" / "Test").cast<Class>()) { + name equals "Test" + + constructors counts 2 + constructors.find { it.parameters.isNullOrEmpty() }.assertNotNull("Test()") + + with(constructors.find { it.parameters.isNotEmpty() }.assertNotNull("Test(String)")) { + parameters.firstOrNull()?.type?.constructorFqName equals "java.lang.String" + } + } + } + } + + @Test + fun innerClass() { + inlineModelTest( + """ + |class InnerClass { + | public class D {} + |} + """ + ) { + with((this / "java" / "InnerClass").cast<Class>()) { + children counts 1 + with((this / "D").cast<Class>()) { + name equals "D" + children counts 0 + } + } + } + } + + @Test + fun varargs() { + inlineModelTest( + """ + |class Foo { + | public void bar(String... x); + |} + """ + ) { + with((this / "java" / "Foo").cast<Class>()) { + name equals "Foo" + children counts 1 + + with((this / "bar").cast<Function>()) { + name equals "bar" + with(parameters.firstOrNull().assertNotNull("parameter")) { + name equals "x" + type.constructorFqName equals "java.lang.String..." + } + } + } + } + } + + @Test // todo + fun fields() { + inlineModelTest( + """ + |class Test { + | public int i; + | public static final String s; + |} + """ + ) { + with((this / "java" / "Test").cast<Class>()) { + children counts 2 + + with((this / "i").cast<Property>()) { + getter.assertNotNull("i.get") + setter.assertNotNull("i.set") + } + + with((this / "s").cast<Property>()) { + getter.assertNotNull("s.get") + setter.assertNotNull("s.set") + + } + } + } + } + + // @Test fun fields() { + // verifyJavaPackageMember("testdata/java/field.java", defaultModelConfig) { cls -> + // val i = cls.members(NodeKind.Property).single { it.name == "i" } + // assertEquals("Int", i.detail(NodeKind.Type).name) + // assertTrue("var" in i.details(NodeKind.Modifier).map { it.name }) + // + // val s = cls.members(NodeKind.Property).single { it.name == "s" } + // assertEquals("String", s.detail(NodeKind.Type).name) + // assertFalse("var" in s.details(NodeKind.Modifier).map { it.name }) + // assertTrue("static" in s.details(NodeKind.Modifier).map { it.name }) + // } + // } + + // @Test fun staticMethod() { todo + // verifyJavaPackageMember("testdata/java/staticMethod.java", defaultModelConfig) { cls -> + // val m = cls.members(NodeKind.Function).single { it.name == "foo" } + // assertTrue("static" in m.details(NodeKind.Modifier).map { it.name }) + // } + // } + // + // /** + // * `@suppress` not supported in Java! + // * + // * [Proposed tags](https://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html) + // * Proposed tag `@exclude` for it, but not supported yet + // */ + // @Ignore("@suppress not supported in Java!") @Test fun suppressTag() { + // verifyJavaPackageMember("testdata/java/suppressTag.java", defaultModelConfig) { cls -> + // assertEquals(1, cls.members(NodeKind.Function).size) + // } + // } + // + // @Test fun annotatedAnnotation() { + // verifyJavaPackageMember("testdata/java/annotatedAnnotation.java", defaultModelConfig) { cls -> + // assertEquals(1, cls.annotations.size) + // with(cls.annotations[0]) { + // assertEquals(1, details.count()) + // with(details[0]) { + // assertEquals(NodeKind.Parameter, kind) + // assertEquals(1, details.count()) + // with(details[0]) { + // assertEquals(NodeKind.Value, kind) + // assertEquals("[AnnotationTarget.FIELD, AnnotationTarget.CLASS, AnnotationTarget.FILE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER]", name) + // } + // } + // } + // } + // } + // + // @Test fun deprecation() { + // verifyJavaPackageMember("testdata/java/deprecation.java", defaultModelConfig) { cls -> + // val fn = cls.members(NodeKind.Function).single() + // assertEquals("This should no longer be used", fn.deprecation!!.content.toTestString()) + // } + // } + // + // @Test fun javaLangObject() { + // verifyJavaPackageMember("testdata/java/javaLangObject.java", defaultModelConfig) { cls -> + // val fn = cls.members(NodeKind.Function).single() + // assertEquals("Any", fn.detail(NodeKind.Type).name) + // } + // } + + @Test + fun enumValues() { + inlineModelTest( + """ + |enum E { + | Foo + |} + """ + ) { + with((this / "java" / "E").cast<Enum>()) { + name equals "E" + entries counts 1 + + with((this / "Foo").cast<EnumEntry>()) { + name equals "Foo" + } + } + } + } + + + // todo + // @Test fun inheritorLinks() { + // verifyJavaPackageMember("testdata/java/InheritorLinks.java", defaultModelConfig) { cls -> + // val fooClass = cls.members.single { it.name == "Foo" } + // val inheritors = fooClass.references(RefKind.Inheritor) + // assertEquals(1, inheritors.size) + // } + // } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/PackagesTest.kt b/plugins/base/src/test/kotlin/model/PackagesTest.kt new file mode 100644 index 00000000..e19cc82d --- /dev/null +++ b/plugins/base/src/test/kotlin/model/PackagesTest.kt @@ -0,0 +1,126 @@ +package model + +import org.jetbrains.dokka.model.Package +import org.junit.Test +import utils.AbstractModelTest + +class PackagesTest : AbstractModelTest("/src/main/kotlin/packages/Test.kt", "packages") { + + @Test + fun rootPackage() { + inlineModelTest( + """ + | + """.trimIndent(), + prependPackage = false + ) { + with((this / "").cast<Package>()) { + name equals "" + children counts 0 + } + } + } + + @Test + fun simpleNamePackage() { + inlineModelTest( + """ + |package simple + """.trimIndent(), + prependPackage = false + ) { + with((this / "simple").cast<Package>()) { + name equals "simple" + children counts 0 + } + } + } + + @Test + fun dottedNamePackage() { + inlineModelTest( + """ + |package dot.name + """.trimIndent(), + prependPackage = false + ) { + with((this / "dot.name").cast<Package>()) { + name equals "dot.name" + children counts 0 + } + } + + } + + @Test + fun multipleFiles() { + inlineModelTest( + """ + |package dot.name + |/src/main/kotlin/packages/Test2.kt + |package simple + """.trimIndent(), + prependPackage = false + ) { + children counts 2 + with((this / "dot.name").cast<Package>()) { + name equals "dot.name" + children counts 0 + } + with((this / "simple").cast<Package>()) { + name equals "simple" + children counts 0 + } + } + } + + @Test + fun multipleFilesSamePackage() { + inlineModelTest( + """ + |package simple + |/src/main/kotlin/packages/Test2.kt + |package simple + """.trimIndent(), + prependPackage = false + ) { + children counts 1 + with((this / "simple").cast<Package>()) { + name equals "simple" + children counts 0 + } + } + } + + @Test + fun classAtPackageLevel() { + inlineModelTest( + """ + |package simple.name + | + |class Foo {} + """.trimIndent(), + prependPackage = false + ) { + with((this / "simple.name").cast<Package>()) { + name equals "simple.name" + children counts 1 + } + } + } + + // todo +// @Test fun suppressAtPackageLevel() { +// verifyModel( +// ModelConfig( +// roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)), +// perPackageOptions = listOf( +// PackageOptionsImpl(prefix = "simple.name", suppress = true) +// ), +// analysisPlatform = analysisPlatform +// ) +// ) { model -> +// assertEquals(0, model.members.count()) +// } +// } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/PropertyTest.kt b/plugins/base/src/test/kotlin/model/PropertyTest.kt new file mode 100644 index 00000000..633796e7 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/PropertyTest.kt @@ -0,0 +1,176 @@ +package model + +import org.jetbrains.dokka.model.Package +import org.jetbrains.dokka.model.Property +import org.jetbrains.kotlin.descriptors.Visibilities +import org.junit.Test +import utils.AbstractModelTest +import utils.assertNotNull + +class PropertyTest : AbstractModelTest("/src/main/kotlin/property/Test.kt", "property") { + + @Test + fun valueProperty() { + inlineModelTest( + """ + |val property = "test"""" + ) { + with((this / "property" / "property").cast<Property>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.constructorFqName equals "kotlin.String" + } + type.constructorFqName equals "kotlin.String" + } + } + } + + @Test + fun variableProperty() { + inlineModelTest( + """ + |var property = "test" + """ + ) { + with((this / "property" / "property").cast<Property>()) { + name equals "property" + children counts 0 + setter.assertNotNull("Setter") + with(getter.assertNotNull("Getter")) { + type.constructorFqName equals "kotlin.String" + } + type.constructorFqName equals "kotlin.String" + } + } + } + + @Test + fun valuePropertyWithGetter() { + inlineModelTest( + """ + |val property: String + | get() = "test" + """ + ) { + with((this / "property" / "property").cast<Property>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.constructorFqName equals "kotlin.String" + } + type.constructorFqName equals "kotlin.String" + } + } + } + + @Test + fun variablePropertyWithAccessors() { + inlineModelTest( + """ + |var property: String + | get() = "test" + | set(value) {} + """ + ) { + with((this / "property" / "property").cast<Property>()) { + name equals "property" + children counts 0 + setter.assertNotNull("Setter") + with(getter.assertNotNull("Getter")) { + type.constructorFqName equals "kotlin.String" + } + visibility.values allEquals Visibilities.PUBLIC + } + } + } + + @Test + fun propertyWithReceiver() { + inlineModelTest( + """ + |val String.property: Int + | get() = size() * 2 + """ + ) { + with((this / "property" / "property").cast<Property>()) { + name equals "property" + children counts 0 + with(receiver.assertNotNull("property receiver")) { + name equals null + type.constructorFqName equals "kotlin.String" + } + with(getter.assertNotNull("Getter")) { + type.constructorFqName equals "kotlin.Int" + } + visibility.values allEquals Visibilities.PUBLIC + } + } + } + + @Test + fun propertyOverride() { + inlineModelTest( + """ + |open class Foo() { + | open val property: Int get() = 0 + |} + |class Bar(): Foo() { + | override val property: Int get() = 1 + |} + """ + ) { + with((this / "property").cast<Package>()) { + with((this / "Foo" / "property").cast<Property>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.constructorFqName equals "kotlin.Int" + } + } + with((this / "Bar" / "property").cast<Property>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.constructorFqName equals "kotlin.Int" + } + } + } + } + } + + // todo +// @Test fun sinceKotlin() { +// checkSourceExistsAndVerifyModel("testdata/properties/sinceKotlin.kt", defaultModelConfig) { model -> +// with(model.members.single().members.single()) { +// assertEquals("1.1", sinceKotlin) +// } +// } +// } +//} +// +//class JSPropertyTest: BasePropertyTest(Platform.js) {} +// +//class JVMPropertyTest : BasePropertyTest(Platform.jvm) { +// @Test +// fun annotatedProperty() { +// checkSourceExistsAndVerifyModel( +// "testdata/properties/annotatedProperty.kt", +// modelConfig = ModelConfig( +// analysisPlatform = analysisPlatform, +// withKotlinRuntime = true +// ) +// ) { model -> +// with(model.members.single().members.single()) { +// Assert.assertEquals(1, annotations.count()) +// with(annotations[0]) { +// Assert.assertEquals("Strictfp", name) +// Assert.assertEquals(Content.Empty, content) +// Assert.assertEquals(NodeKind.Annotation, kind) +// } +// } +// } +// } +// +//} +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/utils/ModelUtils.kt b/plugins/base/src/test/kotlin/utils/ModelUtils.kt new file mode 100644 index 00000000..6893c65f --- /dev/null +++ b/plugins/base/src/test/kotlin/utils/ModelUtils.kt @@ -0,0 +1,33 @@ +package utils + +import org.jetbrains.dokka.model.Module +import org.jetbrains.dokka.model.doc.DocumentationNode +import testApi.testRunner.AbstractCoreTest + +abstract class AbstractModelTest(val path: String? = null, val pkg: String) : ModelDSL(), AssertDSL { + + fun inlineModelTest( + query: String, + platform: String = "jvm", + targetList: List<String> = listOf("jvm"), + prependPackage: Boolean = true, + block: Module.() -> Unit + ) { + val configuration = dokkaConfiguration { + passes { + pass { + sourceRoots = listOf("src/") + analysisPlatform = platform + targets = targetList + } + } + } + val prepend = path.let { p -> p?.let { "|$it\n" } ?: "" } + if(prependPackage) "|package $pkg" else "" + + testInline(("$prepend\n$query").trim().trimIndent(), configuration) { + documentablesTransformationStage = block + } + } + + +} diff --git a/plugins/base/src/test/kotlin/utils/TestUtils.kt b/plugins/base/src/test/kotlin/utils/TestUtils.kt new file mode 100644 index 00000000..641c68a2 --- /dev/null +++ b/plugins/base/src/test/kotlin/utils/TestUtils.kt @@ -0,0 +1,68 @@ +package utils + +import org.jetbrains.dokka.model.Class +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.Function +import org.jetbrains.dokka.model.Property +import org.jetbrains.dokka.model.doc.* +import testApi.testRunner.AbstractCoreTest +import kotlin.reflect.KClass +import kotlin.reflect.full.safeCast + +@DslMarker +annotation class TestDSL + +@TestDSL +abstract class ModelDSL : AbstractCoreTest() { + operator fun Documentable?.div(name: String): Documentable? = + this?.children?.find { it.name == name } + + inline fun <reified T : Documentable> Documentable?.cast(): T = + (this as? T).assertNotNull() +} + +@TestDSL +interface AssertDSL { + infix fun Any?.equals(other: Any?) = this.assertEqual(other) + infix fun Collection<Any>?.allEquals(other: Any?) = + this?.also { c -> c.forEach { it equals other } } ?: run { assert(false) { "Collection is empty" } } + + infix fun <T> Collection<T>?.counts(n: Int) = this.orEmpty().assertCount(n) + + infix fun <T> T?.notNull(name: String): T = this.assertNotNull(name) + + fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") = + assert(count() == n) { "${prefix}Expected $n, got ${count()}" } + + fun <T> T?.assertEqual(expected: T, prefix: String = "") = assert(this == expected) { + "${prefix}Expected $expected, got $this" + } +} + +inline fun <reified T : Any> Any?.assertIsInstance(name: String): T = + this.let { it as? T } ?: throw AssertionError("$name should not be null") + +fun List<DocumentationNode>.commentsToString(): String = + this.flatMap { it.children }.joinToString(separator = "\n") { it.root.docTagSummary() } + +fun TagWrapper.text(): String = when (val t = this) { + is NamedTagWrapper -> "${t.name}: [${t.root.text()}]" + else -> t.root.text() +} + +fun DocTag.text(): String = when (val t = this) { + is Text -> t.body + is Code -> t.children.joinToString("\n") { it.text() } + is P -> t.children.joinToString(separator = "\n") { it.text() } + else -> t.toString() +} + +fun <T : Documentable> T?.comments(): String = docs().map { it.text() } + .joinToString(separator = "\n") { it } + +fun <T> T?.assertNotNull(name: String = ""): T = this ?: throw AssertionError("$name should not be null") + +fun <T : Documentable> T?.docs() = this?.documentation.orEmpty().values.flatMap { it.children } + +val Class.supers + get() = supertypes.flatMap{it.component2()}
\ No newline at end of file |