blob: 4716cc30469704e5607f00a2f457d0550a13a1eb [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.devsite.renderer.converters
import com.google.common.truth.Truth.assertThat
import com.google.devsite.components.symbols.Parameter
import com.google.devsite.renderer.Language
import com.google.devsite.renderer.converters.testing.asType
import com.google.devsite.renderer.converters.testing.item
import com.google.devsite.renderer.converters.testing.items
import com.google.devsite.renderer.converters.testing.link
import com.google.devsite.testing.ConverterTestBase
import org.jetbrains.dokka.model.DModule
import org.jetbrains.dokka.model.DParameter
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
@RunWith(Parameterized::class)
internal class ParameterDocumentableConverterTest(
private val language: Language
) : ConverterTestBase(language) {
@Test
fun `Parameter has correct type`() {
val param = """
|fun foo(a: String) = Unit
""".render().param()
val paramType = param.data.primary.asType()
assertThat(paramType.link().name).isEqualTo("String")
javaOnly {
assertThat(paramType.link().url).isEqualTo("/reference/java/lang/String.html")
}
kotlinOnly {
assertThat(paramType.link().url).isEqualTo("/reference/kotlin/kotlin/String.html")
}
}
@Test
fun `Parameter has correct type for Any`() {
val param = """
|fun foo(a: Any) = Unit
""".render().param()
val paramType = param.data.primary.asType()
javaOnly {
assertThat(paramType.link().name).isEqualTo("Object")
assertThat(paramType.link().url).isEqualTo("/reference/java/lang/Object.html")
}
kotlinOnly {
assertThat(paramType.link().name).isEqualTo("Any")
assertThat(paramType.link().url).isEqualTo("/reference/kotlin/kotlin/Any.html")
}
}
@Test
fun `Parameter understands annotation`() {
val param = """
|annotation class Hello
|fun foo(@Hello a: List<Int>)
""".render().param()
assertThat(param.data.annotations).isNotEmpty()
}
@Test
fun `Parameter understands generics`() {
val param = """
|fun foo(a: List<String>)
""".render().param()
val paramType = param.data.primary.asType()
val generic = paramType.data.generics.item()
assertThat(generic.link().name).isEqualTo("String")
}
@Test
fun `Parameter understands nested generics`() {
val param = """
|fun foo(a: List<Set<String>>)
""".render().param()
val paramType = param.data.primary.asType()
val generic = paramType.data.generics.item().asType()
val nestedGeneric = generic.data.generics.item()
assertThat(nestedGeneric.link().name).isEqualTo("String")
}
@Test
fun `Parameter understands * generics`() {
val param = """
|fun foo(a: List<*>)
""".render().param()
val paramType = param.data.primary.asType()
val generic = paramType.data.generics.item()
javaOnly { assertThat(generic.link().name).isEqualTo("?") }
kotlinOnly { assertThat(generic.link().name).isEqualTo("*") }
}
@Test
fun `Parameter understands lambda generics`() {
val param = """
|fun foo(a: List<() -> String>)
""".render().param()
val generic = param.data.primary.asType().data.generics.item()
val lambdaParam = (generic as Parameter).data
javaOnly {
assertNoLambdaStuff(lambdaParam)
val primary = lambdaParam.primary.asType().data
assertThat(primary.type.data.name).isEqualTo("Function0")
assertThat(primary.generics.item().link().name).isEqualTo("String")
}
kotlinOnly {
assertThat(lambdaParam.isLambda).isTrue()
assertThat(lambdaParam.receiver).isNull()
assertThat(lambdaParam.lambdaModifiers).isEmpty()
assertThat(lambdaParam.lambdaParams).isEmpty()
assertThat(lambdaParam.primary.link().name).isEqualTo("String")
}
}
@Test
fun `Parameter understands variance generics`() {
val param = """
|fun foo(a: Map<in String, out Double>)
""".render().param()
val paramType = param.data.primary.asType()
val generic = paramType.data.generics.items(2)
// TODO(b/166530498): support variance
assertThat(generic.first().link().name).isEqualTo("String")
assertThat(generic.last().link().name).isEqualTo("Double")
}
@Test
fun `Parameter understands inline generics`() {
val param = """
|fun <T> foo(a: T)
""".render().param()
val paramType = param.data.primary.asType()
assertThat(paramType.link().name).isEqualTo("T")
assertThat(paramType.link().url).isEmpty()
}
@Test
fun `Parameter understands factory lambda`() {
val param = """
|fun foo(a: () -> Unit)
""".render().param().data
javaOnly {
assertNoLambdaStuff(param)
val primary = param.primary.asType().data
assertThat(primary.type.data.name).isEqualTo("Function0")
assertThat(primary.generics.item().link().name).isEqualTo("Unit")
}
kotlinOnly {
assertThat(param.isLambda).isTrue()
assertThat(param.receiver).isNull()
assertThat(param.lambdaModifiers).isEmpty()
assertThat(param.lambdaParams).isEmpty()
assertThat(param.primary.link().name).isEqualTo("Unit")
}
}
@Test
fun `Parameter understands suspend lambda`() {
val param = """
|fun foo(a: suspend () -> Unit)
""".render().param().data
javaOnly {
assertNoLambdaStuff(param)
val primary = param.primary.asType().data
assertThat(primary.type.data.name).isEqualTo("SuspendFunction0")
assertThat(primary.generics.single().link().name).isEqualTo("Unit")
}
kotlinOnly {
assertThat(param.isLambda).isTrue()
assertThat(param.receiver).isNull()
assertThat(param.lambdaParams).isEmpty()
assertThat(param.lambdaModifiers).containsExactly("suspend")
assertThat(param.primary.link().name).isEqualTo("Unit")
}
}
@Test
fun `Parameter understands suspend lambda with receiver`() {
val param = """
|fun foo(a: suspend Float.() -> Unit)
""".render().param().data
javaOnly {
assertNoLambdaStuff(param)
assertThat(param.primary.link().name).isEqualTo("SuspendFunction1")
}
kotlinOnly {
assertThat(param.isLambda).isTrue()
assertThat(param.receiver).isNotNull()
assertThat(param.receiver!!.link().name).isEqualTo("Float")
assertThat(param.lambdaModifiers).containsExactly("suspend")
assertThat(param.lambdaParams).isEmpty()
}
}
@Test
fun `Parameter understands suspend lambda with params`() {
val param = """
|fun foo(a: suspend (Float) -> Unit)
""".render().param().data
javaOnly {
assertNoLambdaStuff(param)
val primary = param.primary.asType().data
assertThat(primary.type.data.name).isEqualTo("SuspendFunction1")
assertThat(primary.generics.first().link().name).isEqualTo("Float")
assertThat(primary.generics.last().link().name).isEqualTo("Unit")
}
kotlinOnly {
assertThat(param.isLambda).isTrue()
assertThat(param.receiver).isNull()
assertThat(param.lambdaModifiers).containsExactly("suspend")
assertThat(param.lambdaParams).hasSize(1)
assertThat(param.lambdaParams.single().link().name).isEqualTo("Float")
}
}
@Test
fun `Parameter understands parameterized lambda`() {
val param = """
|fun foo(a: (String) -> Unit)
""".render().param().data
javaOnly {
assertNoLambdaStuff(param)
val primary = param.primary.asType().data
assertThat(primary.type.data.name).isEqualTo("Function1")
assertThat(primary.generics.first().link().name).isEqualTo("String")
assertThat(primary.generics.last().link().name).isEqualTo("Unit")
}
kotlinOnly {
assertThat(param.isLambda).isTrue()
assertThat(param.receiver).isNull()
assertThat(param.lambdaModifiers).isEmpty()
assertThat(param.lambdaParams).hasSize(1)
assertThat(param.lambdaParams.single().link().name).isEqualTo("String")
}
}
@Test
fun `Parameter understands lambda with receiver`() {
val param = """
|fun foo(a: Float.() -> Unit)
""".render().param().data
javaOnly {
assertNoLambdaStuff(param)
val primary = param.primary.asType().data
assertThat(primary.type.data.name).isEqualTo("Function1")
assertThat(primary.generics.first().link().name).isEqualTo("Float")
assertThat(primary.generics.last().link().name).isEqualTo("Unit")
}
kotlinOnly {
assertThat(param.isLambda).isTrue()
assertThat(param.receiver).isNotNull()
assertThat(param.receiver!!.link().name).isEqualTo("Float")
assertThat(param.lambdaModifiers).isEmpty()
assertThat(param.lambdaParams).isEmpty()
}
}
@Test
fun `Parameter understands lambda with difficult generic combination`() {
val param = """
|fun foo(block: Int.(Map<String, Int>, Double) -> Collection<Float>)
""".render().param().data
javaOnly {
assertNoLambdaStuff(param)
val primary = param.primary.asType().data
assertThat(primary.type.data.name).isEqualTo("Function3")
assertThat(primary.generics).hasSize(4)
assertThat(primary.generics[0].link().name).isEqualTo("Integer")
assertThat(primary.generics[1].link().name).isEqualTo("Map")
assertThat(primary.generics[2].link().name).isEqualTo("Double")
assertThat(primary.generics[3].link().name).isEqualTo("Collection")
val mapGenerics = primary.generics[1].asType().data.generics.items(2)
assertThat(mapGenerics.first().link().name).isEqualTo("String")
assertThat(mapGenerics.last().link().name).isEqualTo("Integer")
val collectionGeneric = primary.generics[3].asType().data.generics.item()
assertThat(collectionGeneric.link().name).isEqualTo("Float")
}
kotlinOnly {
assertThat(param.isLambda).isTrue()
assertThat(param.receiver).isNotNull()
assertThat(param.receiver!!.link().name).isEqualTo("Int")
assertThat(param.lambdaModifiers).isEmpty()
assertThat(param.lambdaParams).hasSize(2)
assertThat(param.lambdaParams.first().link().name).isEqualTo("Map")
assertThat(param.lambdaParams.last().link().name).isEqualTo("Double")
assertThat(param.primary.asType().link().name).isEqualTo("Collection")
val mapGenerics =
param.lambdaParams.first().asType().data.generics.items(2)
assertThat(mapGenerics.first().link().name).isEqualTo("String")
assertThat(mapGenerics.last().link().name).isEqualTo("Int")
val collectionGeneric = param.primary.asType().data.generics.item()
assertThat(collectionGeneric.link().name).isEqualTo("Float")
}
}
@Test
fun `Parameter includes primitive default value`() {
val param = """
|fun foo(stuff: String = "stuff")
""".render().param().data
javaOnly { assertThat(param.defaultValue).isNull() }
kotlinOnly { assertThat(param.defaultValue).isEqualTo("\"stuff\"") }
}
@Test
fun `Parameter includes default value`() {
val param = """
|fun foo(stuff: List<String> = listOf("a", "b", "c"))
""".render().param().data
javaOnly { assertThat(param.defaultValue).isNull() }
kotlinOnly { assertThat(param.defaultValue).isEqualTo("""listOf("a", "b", "c")""") }
}
@Test
fun `Parameter excludes default value in summary`() {
val param = """
|fun foo(stuff: List<String> = listOf("a", "b", "c"))
""".render().param(forSummary = true).data
assertThat(param.defaultValue).isNull()
}
@Test
fun `Parameter includes nullability information`() {
val param = """
|fun foo(foo: Int?)
""".render().param(forSummary = true).data
javaOnly { assertThat(param.annotations).isNotEmpty() }
kotlinOnly {
assertThat(param.annotations).isEmpty()
assertThat(param.primary.asType().data.nullable).isTrue()
}
}
@Test
fun `Nullable primitive type is upgraded in Java`() {
val param = """
|fun foo(foo: Int?)
""".render().param().data
val typeName = param.primary.asType().link().name
javaOnly { assertThat(typeName).isEqualTo("Integer") }
kotlinOnly { assertThat(typeName).isEqualTo("Int") }
}
@Test
fun `Primitive type in generic is upgraded in Java`() {
val param = """
|fun foo(foo: List<Int>)
""".render().param().data
val generic = param.primary.asType().data.generics.item()
javaOnly { assertThat(generic.link().name).isEqualTo("Integer") }
kotlinOnly { assertThat(generic.link().name).isEqualTo("Int") }
}
@Test
fun `Unit isn't changed in Java when used as a parameter`() {
val param = """
|fun foo(foo: Unit)
""".render().param().data
assertThat(param.primary.asType().link().name).isEqualTo("Unit")
}
@Test
fun `Primitive type is mapped in Java`() {
val param = """
|fun foo(foo: Int)
""".render().param().data
val typeName = param.primary.asType().link().name
javaOnly { assertThat(typeName).isEqualTo("int") }
kotlinOnly { assertThat(typeName).isEqualTo("Int") }
}
@Test
fun `Primitive array is mapped in Java`() {
val param = """
|fun foo(foo: IntArray)
""".render().param().data
val typeName = param.primary.asType().link().name
javaOnly { assertThat(typeName).isEqualTo("int[]") }
kotlinOnly { assertThat(typeName).isEqualTo("IntArray") }
}
@Test
fun `Vararg modifier appears for param`() {
val param = """
|fun foo(vararg stuff: Int) = Unit
""".render().param()
assertThat(param.data.name).isEqualTo("stuff")
javaOnly { assertThat(param.data.modifiers).isEmpty() }
kotlinOnly { assertThat(param.data.modifiers.last()).isEqualTo("vararg") }
}
@Test
fun `Crossline modifier appears for param`() {
val param = """
|fun foo(crossinline stuff: () -> Unit) = Unit
""".render().param()
assertThat(param.data.name).isEqualTo("stuff")
javaOnly { assertThat(param.data.modifiers).isEmpty() }
kotlinOnly { assertThat(param.data.modifiers.last()).isEqualTo("crossinline") }
}
@Test
fun `higher order param name appears for param`() {
val param = """
|fun foo(block: (factory: () -> String) -> Int) = Unit
""".render().param().data
assertThat(param.name).isEqualTo("block")
javaOnly {
assertNoLambdaStuff(param)
}
kotlinOnly {
assertThat(param.lambdaParams).hasSize(1)
val lambdaParam = param.lambdaParams.single() as Parameter
assertThat(lambdaParam.data.name).isEqualTo("factory")
}
}
private fun DModule.param(forSummary: Boolean = false): Parameter {
val converter = ParameterDocumentableConverter(language, pathProvider())
return converter.componentForParameter(parameterDoc(), forSummary)
}
private fun DModule.parameterDoc(): DParameter {
return packages.single().functions.single().parameters.single()
}
private fun assertNoLambdaStuff(data: Parameter.Params) {
assertThat(data.isLambda).isFalse()
assertThat(data.receiver).isNull()
assertThat(data.lambdaParams).isEmpty()
assertThat(data.lambdaModifiers).isEmpty()
}
companion object {
@JvmStatic
@Parameterized.Parameters(name = "{0}")
fun data() = listOf(
arrayOf(Language.JAVA),
arrayOf(Language.KOTLIN)
)
}
}