package model import org.jetbrains.dokka.links.DRI import org.jetbrains.dokka.model.* import org.junit.jupiter.api.Test import utils.AbstractModelTest import utils.assertNotNull import utils.comments import utils.name class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "function") { @Test fun function() { inlineModelTest( """ |/** | * Function fn | */ |fun fn() {} """ ) { with((this / "function" / "fn").cast()) { name equals "fn" type.name equals "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()) { 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.name equals "Int" } } } } @Test fun functionWithReceiver() { inlineModelTest( """ |/** | * Function with receiver | */ |fun String.fn() {} | |/** | * Function with receiver | */ |fun String.fn(x: Int) {} """ ) { with((this / "function").cast()) { 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.name equals "Int" } } } } @Test fun functionWithParams() { inlineModelTest( """ |/** | * Multiline | * | * Function | * Documentation | */ |fun function(/** parameter */ x: Int) { |} """ ) { with((this / "function" / "function").cast()) { comments() equals "Multiline\nFunction Documentation" name equals "function" parameters counts 1 parameters.firstOrNull().assertNotNull("Parameter: ").also { it.name equals "x" it.type.name equals "Int" it.comments() equals "parameter" } type.assertNotNull("Return type: ").name equals "Unit" } } } @Test fun functionWithNotDocumentedAnnotation() { inlineModelTest( """ |@Suppress("FOO") fun f() {} """ ) { with((this / "function" / "f").cast()) { with(extra[Annotations].assertNotNull("Annotations")) { content counts 1 with(content.first()) { dri.classNames equals "Suppress" params.entries counts 1 (params["names"].assertNotNull("param") as ArrayValue).value equals listOf(StringValue("\"FOO\"")) } } } } } @Test fun inlineFunction() { inlineModelTest( """ |inline fun f(a: () -> String) {} """ ) { with((this / "function" / "f").cast()) { extra[AdditionalModifiers]?.content counts 1 extra[AdditionalModifiers]?.content exists ExtraModifiers.KotlinOnlyModifiers.Inline } } } @Test fun suspendFunction() { inlineModelTest( """ |suspend fun f() {} """ ) { with((this / "function" / "f").cast()) { extra[AdditionalModifiers]?.content counts 1 extra[AdditionalModifiers]?.content exists ExtraModifiers.KotlinOnlyModifiers.Suspend } } } @Test fun suspendInlineFunctionOrder() { inlineModelTest( """ |suspend inline fun f(a: () -> String) {} """ ) { with((this / "function" / "f").cast()) { extra[AdditionalModifiers]?.content counts 2 extra[AdditionalModifiers]?.content exists ExtraModifiers.KotlinOnlyModifiers.Suspend extra[AdditionalModifiers]?.content exists ExtraModifiers.KotlinOnlyModifiers.Inline } } } @Test fun inlineSuspendFunctionOrderChanged() { inlineModelTest( """ |inline suspend fun f(a: () -> String) {} """ ) { with((this / "function" / "f").cast()) { with(extra[AdditionalModifiers].assertNotNull("AdditionalModifiers")) { content counts 2 content exists ExtraModifiers.KotlinOnlyModifiers.Suspend content exists ExtraModifiers.KotlinOnlyModifiers.Inline } } } } @Test fun functionWithAnnotatedParam() { inlineModelTest( """ |@Target(AnnotationTarget.VALUE_PARAMETER) |@Retention(AnnotationRetention.SOURCE) |@MustBeDocumented |public annotation class Fancy | |fun function(@Fancy notInlined: () -> Unit) {} """ ) { with((this / "function" / "Fancy").cast()) { with(extra[Annotations].assertNotNull("Annotations")) { content counts 3 with(content.map { it.dri.classNames to it }.toMap()) { with(this["Target"].assertNotNull("Target")) { (params["allowedTargets"].assertNotNull("allowedTargets") as ArrayValue).value equals listOf( EnumValue( "AnnotationTarget.VALUE_PARAMETER", DRI("kotlin.annotation", "AnnotationTarget.VALUE_PARAMETER") ) ) } with(this["Retention"].assertNotNull("Retention")) { (params["value"].assertNotNull("value") as EnumValue) equals EnumValue( "AnnotationRetention.SOURCE", DRI("kotlin.annotation", "AnnotationRetention.SOURCE") ) } this["MustBeDocumented"].assertNotNull("MustBeDocumented").params.entries counts 0 } } } with((this / "function" / "function" / "notInlined").cast()) { with(this.extra[Annotations].assertNotNull("Annotations")) { content counts 1 with(content.first()) { dri.classNames equals "Fancy" params.entries counts 0 } } } } } @Test fun functionWithNoinlineParam() { inlineModelTest( """ |fun f(noinline notInlined: () -> Unit) {} """ ) { with((this / "function" / "f" / "notInlined").cast()) { extra[AdditionalModifiers]?.content counts 1 extra[AdditionalModifiers]?.content exists ExtraModifiers.KotlinOnlyModifiers.NoInline } } } @Test fun annotatedFunctionWithAnnotationParameters() { inlineModelTest( """ |@Target(AnnotationTarget.VALUE_PARAMETER) |@Retention(AnnotationRetention.SOURCE) |@MustBeDocumented |public annotation class Fancy(val size: Int) | |@Fancy(1) fun f() {} """ ) { with((this / "function" / "Fancy").cast()) { constructors counts 1 with(constructors.first()) { parameters counts 1 with(parameters.first()) { type.name equals "Int" name equals "size" } } with(extra[Annotations].assertNotNull("Annotations")) { content counts 3 with(content.map { it.dri.classNames to it }.toMap()) { with(this["Target"].assertNotNull("Target")) { (params["allowedTargets"].assertNotNull("allowedTargets") as ArrayValue).value equals listOf( EnumValue( "AnnotationTarget.VALUE_PARAMETER", DRI("kotlin.annotation", "AnnotationTarget.VALUE_PARAMETER") ) ) } with(this["Retention"].assertNotNull("Retention")) { (params["value"].assertNotNull("value") as EnumValue) equals EnumValue( "AnnotationRetention.SOURCE", DRI("kotlin.annotation", "AnnotationRetention.SOURCE") ) } this["MustBeDocumented"].assertNotNull("MustBeDocumented").params.entries counts 0 } } } with((this / "function" / "f").cast()) { with(this.extra[Annotations].assertNotNull("Annotations")) { content counts 1 with(content.first()) { dri.classNames equals "Fancy" params.entries counts 1 (params["size"] as StringValue).value equals "1" } } } } } @Test fun functionWithDefaultStringParameter() { inlineModelTest( """ |/src/main/kotlin/function/Test.kt |package function |fun f(x: String = "") {} """ ) { with((this / "function" / "f").cast()) { parameters.forEach { p -> p.name equals "x" p.type.name.assertNotNull("Parameter type: ") equals "String" p.extra[DefaultValue]?.value equals "\"\"" } } } } @Test fun functionWithDefaultFloatParameter() { inlineModelTest( """ |/src/main/kotlin/function/Test.kt |package function |fun f(x: Float = 3.14f) {} """ ) { with((this / "function" / "f").cast()) { parameters.forEach { p -> p.name equals "x" p.type.name.assertNotNull("Parameter type: ") equals "Float" p.extra[DefaultValue]?.value equals "3.14f" } } } } @Test fun sinceKotlin() { inlineModelTest( """ |/** | * Quite useful [String] | */ |@SinceKotlin("1.1") |fun f(): String = "1.1 rulezz" """ ) { with((this / "function" / "f").cast()) { with(extra[Annotations].assertNotNull("Annotations")) { this.content counts 1 with(content.first()) { dri.classNames equals "SinceKotlin" params.entries counts 1 (params["version"].assertNotNull("version") as StringValue).value equals "\"1.1\"" } } } } } }