blob: e816d308560110eb8fdcdd9fbd6e88f6904b1fb8 [file] [log] [blame]
/*
* Copyright (C) 2017 Square, Inc.
*
* 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.squareup.kotlinpoet
import com.google.common.truth.Truth.assertThat
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.jvm.jvmField
import com.squareup.kotlinpoet.jvm.jvmSuppressWildcards
import java.util.concurrent.TimeUnit
import kotlin.test.Test
class KotlinPoetTest {
private val tacosPackage = "com.squareup.tacos"
@Test fun topLevelMembersRetainOrder() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(FunSpec.builder("a").addModifiers(KModifier.PUBLIC).build())
.addType(TypeSpec.classBuilder("B").build())
.addProperty(
PropertySpec.builder("c", String::class, KModifier.PUBLIC)
.initializer("%S", "C")
.build()
)
.addFunction(FunSpec.builder("d").build())
.addType(TypeSpec.classBuilder("E").build())
.addProperty(
PropertySpec.builder("f", String::class, KModifier.PUBLIC)
.initializer("%S", "F")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|import kotlin.Unit
|
|public fun a(): Unit {
|}
|
|public class B
|
|public val c: String = "C"
|
|public fun d(): Unit {
|}
|
|public class E
|
|public val f: String = "F"
|""".trimMargin()
)
}
@Test fun noTopLevelConstructor() {
assertThrows<IllegalArgumentException> {
FileSpec.builder(tacosPackage, "Taco")
.addFunction(FunSpec.constructorBuilder().build())
}
}
@Test fun primaryConstructor() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("cheese", String::class)
.beginControlFlow("require(cheese.isNotEmpty())")
.addStatement("%S", "cheese cannot be empty")
.endControlFlow()
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|public class Taco(
| cheese: String
|) {
| init {
| require(cheese.isNotEmpty()) {
| "cheese cannot be empty"
| }
| }
|}
|""".trimMargin()
)
}
@Test fun primaryConstructorProperties() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("cheese", String::class)
.addParameter("cilantro", String::class)
.addParameter("lettuce", String::class)
.beginControlFlow("require(!cheese.isEmpty())")
.addStatement("%S", "cheese cannot be empty")
.endControlFlow()
.build()
)
.addProperty(
PropertySpec.builder("cheese", String::class)
.initializer("cheese")
.build()
)
.addProperty(
PropertySpec.builder("cilantro", String::class.asTypeName())
.mutable()
.initializer("cilantro")
.build()
)
.addProperty(
PropertySpec.builder("lettuce", String::class)
.initializer("lettuce.trim()")
.build()
)
.addProperty(
PropertySpec.builder("onion", Boolean::class)
.initializer("true")
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Boolean
|import kotlin.String
|
|public class Taco(
| public val cheese: String,
| public var cilantro: String,
| lettuce: String
|) {
| public val lettuce: String = lettuce.trim()
|
| public val onion: Boolean = true
| init {
| require(!cheese.isEmpty()) {
| "cheese cannot be empty"
| }
| }
|}
|""".trimMargin()
)
}
@Test fun propertyModifiers() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.addProperty(
PropertySpec.builder("CHEESE", String::class, KModifier.PRIVATE, KModifier.CONST)
.initializer("%S", "monterey jack")
.build()
)
.addProperty(
PropertySpec.builder("sauce", String::class.asTypeName(), KModifier.PUBLIC)
.mutable()
.initializer("%S", "chipotle mayo")
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|public class Taco {
| private const val CHEESE: String = "monterey jack"
|
| public var sauce: String = "chipotle mayo"
|}
|""".trimMargin()
)
}
@Test fun mistargetedModifier() {
assertThrows<IllegalArgumentException> {
PropertySpec.builder("CHEESE", String::class, KModifier.DATA).build()
}
}
@Test fun visibilityModifiers() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.addFunction(FunSpec.builder("a").addModifiers(KModifier.PUBLIC).build())
.addFunction(FunSpec.builder("b").addModifiers(KModifier.PROTECTED).build())
.addFunction(FunSpec.builder("c").addModifiers(KModifier.INTERNAL).build())
.addFunction(FunSpec.builder("d").addModifiers(KModifier.PRIVATE).build())
.build()
)
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Unit
|
|public class Taco {
| public fun a(): Unit {
| }
|
| protected fun b(): Unit {
| }
|
| internal fun c(): Unit {
| }
|
| private fun d(): Unit {
| }
|}
|""".trimMargin()
)
}
@Test fun strings() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.addFunction(
FunSpec.builder("strings")
.addStatement("val a = %S", "basic string")
.addStatement("val b = %S", "string with a \$ dollar sign")
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"" +
"package com.squareup.tacos\n" +
"\n" +
"import kotlin.Unit\n" +
"\n" +
"public class Taco {\n" +
" public fun strings(): Unit {\n" +
" val a = \"basic string\"\n" +
" val b = \"string with a \${\'\$\'} dollar sign\"\n" +
" }\n" +
"}\n"
)
}
/** When emitting a triple quote, KotlinPoet escapes the 3rd quote in the triplet. */
@Test fun rawStrings() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.addFunction(
FunSpec.builder("strings")
.addStatement("val a = %S", "\"\n")
.addStatement("val b = %S", "a\"\"\"b\"\"\"\"\"\"c\n")
.addStatement(
"val c = %S",
"""
|whoa
|"raw"
|string
""".trimMargin()
)
.addStatement(
"val d = %S",
"""
|"raw"
|string
|with
|${'$'}a interpolated value
""".trimMargin()
)
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"" +
"package com.squareup.tacos\n" +
"\n" +
"import kotlin.Unit\n" +
"\n" +
"public class Taco {\n" +
" public fun strings(): Unit {\n" +
" val a = \"\"\"\n" +
" |\"\n" +
" |\"\"\".trimMargin()\n" +
" val b = \"\"\"\n" +
" |a\"\"\${'\"'}b\"\"\${'\"'}\"\"\${'\"'}c\n" +
" |\"\"\".trimMargin()\n" +
" val c = \"\"\"\n" +
" |whoa\n" +
" |\"raw\"\n" +
" |string\n" +
" \"\"\".trimMargin()\n" +
" val d = \"\"\"\n" +
" |\"raw\"\n" +
" |string\n" +
" |with\n" +
" |\${\'\$\'}a interpolated value\n" +
" \"\"\".trimMargin()\n" +
" }\n" +
"}\n"
)
}
/**
* When a string literal ends in a newline, there's a pipe `|` immediately preceding the closing
* triple quote. Otherwise the closing triple quote has no preceding `|`.
*/
@Test fun edgeCaseStrings() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.addFunction(
FunSpec.builder("strings")
.addStatement("val a = %S", "\n")
.addStatement("val b = %S", " \n ")
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"" +
"package com.squareup.tacos\n" +
"\n" +
"import kotlin.Unit\n" +
"\n" +
"public class Taco {\n" +
" public fun strings(): Unit {\n" +
" val a = \"\"\"\n" +
" |\n" +
" |\"\"\".trimMargin()\n" +
" val b = \"\"\"\n" +
" | \n" +
" | \n" +
" \"\"\".trimMargin()\n" +
" }\n" +
"}\n"
)
}
@Test fun parameterDefaultValue() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("Taco")
.addFunction(
FunSpec.builder("addCheese")
.addParameter(
ParameterSpec.builder("kind", String::class)
.defaultValue("%S", "monterey jack")
.build()
)
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|import kotlin.Unit
|
|public class Taco {
| public fun addCheese(kind: String = "monterey jack"): Unit {
| }
|}
|""".trimMargin()
)
}
@Test fun extensionFunction() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(
FunSpec.builder("shrink")
.returns(String::class)
.receiver(String::class)
.addStatement("return substring(0, length - 1)")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|public fun String.shrink(): String = substring(0, length - 1)
|""".trimMargin()
)
}
@Test fun extensionFunctionLambda() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(
FunSpec.builder("shrink")
.returns(String::class)
.receiver(
LambdaTypeName.get(
parameters = arrayOf(String::class.asClassName()),
returnType = String::class.asTypeName()
)
)
.addStatement("return substring(0, length - 1)")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|public fun ((String) -> String).shrink(): String = substring(0, length - 1)
|""".trimMargin()
)
}
@Test fun extensionFunctionLambdaWithParamName() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(
FunSpec.builder("whatever")
.returns(Unit::class)
.receiver(
LambdaTypeName.get(
parameters = arrayOf(ParameterSpec.builder("name", String::class).build()),
returnType = Unit::class.asClassName()
)
)
.addStatement("return Unit")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|import kotlin.Unit
|
|public fun ((name: String) -> Unit).whatever(): Unit = Unit
|""".trimMargin()
)
}
@Test fun extensionFunctionLambdaWithMultipleParams() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(
FunSpec.builder("whatever")
.returns(Unit::class)
.receiver(
LambdaTypeName.get(
parameters = listOf(
ParameterSpec.builder("name", String::class).build(),
ParameterSpec.unnamed(Int::class),
ParameterSpec.builder("age", Long::class).build()
),
returnType = Unit::class.asClassName()
)
)
.addStatement("return Unit")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Int
|import kotlin.Long
|import kotlin.String
|import kotlin.Unit
|
|public fun ((
| name: String,
| Int,
| age: Long
|) -> Unit).whatever(): Unit = Unit
|""".trimMargin()
)
}
@Test fun extensionProperty() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addProperty(
PropertySpec.builder("extensionProperty", Int::class)
.receiver(String::class)
.getter(
FunSpec.getterBuilder()
.addStatement("return length")
.build()
)
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Int
|import kotlin.String
|
|public val String.extensionProperty: Int
| get() = length
|
""".trimMargin()
)
}
@Test fun extensionPropertyLambda() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addProperty(
PropertySpec.builder("extensionProperty", Int::class)
.receiver(
LambdaTypeName.get(
parameters = arrayOf(String::class.asClassName()),
returnType = String::class.asClassName()
)
)
.getter(
FunSpec.getterBuilder()
.addStatement("return length")
.build()
)
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Int
|import kotlin.String
|
|public val ((String) -> String).extensionProperty: Int
| get() = length
|
""".trimMargin()
)
}
@Test fun nullableTypes() {
val list = (List::class.asClassName().copy(nullable = true) as ClassName)
.parameterizedBy(Int::class.asClassName().copy(nullable = true))
.copy(nullable = true)
assertThat(list.toString()).isEqualTo("kotlin.collections.List<kotlin.Int?>?")
}
@Test fun getAndSet() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addProperty(
PropertySpec.builder("propertyWithCustomAccessors", Int::class.asTypeName())
.mutable()
.initializer("%L", 1)
.getter(
FunSpec.getterBuilder()
.addStatement("println(%S)", "getter")
.addStatement("return field")
.build()
)
.setter(
FunSpec.setterBuilder()
.addParameter("value", Int::class)
.addStatement("println(%S)", "setter")
.addStatement("field = value")
.build()
)
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Int
|
|public var propertyWithCustomAccessors: Int = 1
| get() {
| println("getter")
| return field
| }
| set(`value`) {
| println("setter")
| field = value
| }
|""".trimMargin()
)
}
@Test fun propertyWithLongInitializerWrapping() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addProperty(
PropertySpec
.builder("foo", ClassName(tacosPackage, "Foo").copy(nullable = true))
.addModifiers(KModifier.PRIVATE)
.initializer("DefaultFooRegistry.getInstance().getDefaultFooInstanceForPropertiesFiles(file)")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|private val foo: Foo? =
| DefaultFooRegistry.getInstance().getDefaultFooInstanceForPropertiesFiles(file)
|""".trimMargin()
)
}
@Test fun stackedPropertyModifiers() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addType(
TypeSpec.classBuilder("A")
.addModifiers(KModifier.ABSTRACT)
.addProperty(
PropertySpec.builder("q", String::class.asTypeName())
.mutable()
.addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
.build()
)
.build()
)
.addProperty(
PropertySpec.builder("p", String::class)
.addModifiers(KModifier.CONST, KModifier.INTERNAL)
.initializer("%S", "a")
.build()
)
.addType(
TypeSpec.classBuilder("B")
.superclass(ClassName(tacosPackage, "A"))
.addModifiers(KModifier.ABSTRACT)
.addProperty(
PropertySpec.builder("q", String::class.asTypeName())
.mutable()
.addModifiers(
KModifier.FINAL, KModifier.LATEINIT, KModifier.OVERRIDE, KModifier.PUBLIC
)
.build()
)
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|public abstract class A {
| protected abstract var q: String
|}
|
|internal const val p: String = "a"
|
|public abstract class B : A() {
| public final override lateinit var q: String
|}
|""".trimMargin()
)
}
@Test fun stackedFunModifiers() {
val source = FileSpec.get(
tacosPackage,
TypeSpec.classBuilder("A")
.addModifiers(KModifier.OPEN)
.addFunction(
FunSpec.builder("get")
.addModifiers(
KModifier.EXTERNAL, KModifier.INFIX, KModifier.OPEN, KModifier.OPERATOR,
KModifier.PROTECTED
)
.addParameter("v", String::class)
.returns(String::class)
.build()
)
.addFunction(
FunSpec.builder("loop")
.addModifiers(KModifier.FINAL, KModifier.INLINE, KModifier.INTERNAL, KModifier.TAILREC)
.returns(String::class)
.addStatement("return %S", "a")
.build()
)
.build()
)
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|public open class A {
| protected open external infix operator fun `get`(v: String): String
|
| internal final tailrec inline fun loop(): String = "a"
|}
|""".trimMargin()
)
}
@Test fun basicExpressionBody() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(
FunSpec.builder("addA")
.addParameter("s", String::class)
.returns(String::class)
.addStatement("return s + %S", "a")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|public fun addA(s: String): String = s + "a"
|""".trimMargin()
)
}
@Test fun suspendingLambdas() {
val barType = ClassName(tacosPackage, "Bar")
val suspendingLambda = LambdaTypeName
.get(parameters = arrayOf(ClassName(tacosPackage, "Foo")), returnType = barType)
.copy(suspending = true)
val source = FileSpec.builder(tacosPackage, "Taco")
.addProperty(
PropertySpec.builder("bar", suspendingLambda)
.mutable()
.initializer("{ %T() }", barType)
.build()
)
.addProperty(
PropertySpec.builder("nullBar", suspendingLambda.copy(nullable = true))
.mutable()
.initializer("null")
.build()
)
.addFunction(
FunSpec.builder("foo")
.addParameter("bar", suspendingLambda)
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Unit
|
|public var bar: suspend (Foo) -> Bar = { Bar() }
|
|public var nullBar: (suspend (Foo) -> Bar)? = null
|
|public fun foo(bar: suspend (Foo) -> Bar): Unit {
|}
|""".trimMargin()
)
}
@Test fun enumAsDefaultArgument() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(
FunSpec.builder("timeout")
.addParameter("duration", Long::class)
.addParameter(
ParameterSpec.builder("timeUnit", TimeUnit::class)
.defaultValue("%T.%L", TimeUnit::class, TimeUnit.MILLISECONDS.name)
.build()
)
.addStatement("this.timeout = timeUnit.toMillis(duration)")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import java.util.concurrent.TimeUnit
|import kotlin.Long
|import kotlin.Unit
|
|public fun timeout(duration: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS): Unit {
| this.timeout = timeUnit.toMillis(duration)
|}
|""".trimMargin()
)
}
@Test fun dynamicType() {
val source = FileSpec.builder(tacosPackage, "Taco")
.addFunction(
FunSpec.builder("dynamicTest")
.addCode(
CodeBlock.of(
"%L",
PropertySpec.builder("d1", DYNAMIC)
.initializer("%S", "Taco")
.build()
)
)
.addCode(
CodeBlock.of(
"%L",
PropertySpec.builder("d2", DYNAMIC)
.initializer("1f")
.build()
)
)
.addStatement("// dynamics are dangerous!")
.addStatement("println(d1 - d2)")
.build()
)
.build()
assertThat(source.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Unit
|
|public fun dynamicTest(): Unit {
| val d1: dynamic = "Taco"
| val d2: dynamic = 1f
| // dynamics are dangerous!
| println(d1 - d2)
|}
|""".trimMargin()
)
}
@Test fun primaryConstructorParameterAnnotation() {
val file = FileSpec.builder("com.squareup.tacos", "Taco")
.addType(
TypeSpec.classBuilder("Taco")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("foo", String::class)
.build()
)
.addProperty(
PropertySpec.builder("foo", String::class)
.jvmField()
.initializer("foo")
.build()
)
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|import kotlin.jvm.JvmField
|
|public class Taco(
| @JvmField
| public val foo: String
|)
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/346
@Test fun importTypeArgumentInParameterizedTypeName() {
val file = FileSpec.builder("com.squareup.tacos", "Taco")
.addFunction(
FunSpec.builder("foo")
.addParameter(
"a",
List::class.asTypeName()
.parameterizedBy(Int::class.asTypeName().jvmSuppressWildcards())
)
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Int
|import kotlin.Unit
|import kotlin.collections.List
|import kotlin.jvm.JvmSuppressWildcards
|
|public fun foo(a: List<@JvmSuppressWildcards Int>): Unit {
|}
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/462
@Test fun foldingPropertyWithLambdaInitializer() {
val param = ParameterSpec.builder("arg", ANY).build()
val initializer = CodeBlock.builder()
.beginControlFlow("{ %L ->", param)
.addStatement("println(\"arg=\$%N\")", param)
.endControlFlow()
.build()
val lambdaTypeName = ClassName.bestGuess("com.example.SomeTypeAlias")
val property = PropertySpec.builder("foo", lambdaTypeName)
.initializer("foo")
.build()
val file = FileSpec.builder("com.squareup.tacos", "Taco")
.addType(
TypeSpec.classBuilder("Taco")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter(
ParameterSpec.builder("foo", lambdaTypeName)
.defaultValue(initializer)
.build()
)
.build()
)
.addProperty(property)
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import com.example.SomeTypeAlias
|
|public class Taco(
| public val foo: SomeTypeAlias = { arg: kotlin.Any ->
| println("arg=${'$'}arg")
| }
|
|)
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/483
@Test fun foldingPropertyWithEscapedName() {
val file = FileSpec.builder("com.squareup.tacos", "AlarmInfo")
.addType(
TypeSpec.classBuilder("AlarmInfo")
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter("when", Float::class)
.build()
)
.addProperty(
PropertySpec.builder("when", Float::class)
.initializer("when")
.build()
)
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.Float
|
|public class AlarmInfo(
| public val `when`: Float
|)
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/577
@Test fun noWrappingBetweenParamNameAndType() {
val file = FileSpec.builder("com.squareup.tacos", "Taco")
.addFunction(
FunSpec.builder("functionWithAPrettyLongNameThatWouldCauseWrapping")
.addParameter("parameterWithALongNameThatWouldAlsoCauseWrapping", String::class)
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|import kotlin.Unit
|
|public
| fun functionWithAPrettyLongNameThatWouldCauseWrapping(parameterWithALongNameThatWouldAlsoCauseWrapping: String):
| Unit {
|}
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/576
@Test fun noWrappingBetweenValAndPropertyName() {
val wireField = ClassName("com.squareup.wire", "WireField")
val file = FileSpec.builder("com.squareup.tacos", "Taco")
.addType(
TypeSpec.classBuilder("Taco")
.addModifiers(KModifier.DATA)
.addProperty(
PropertySpec.builder("name", String::class)
.addAnnotation(
AnnotationSpec.builder(wireField)
.addMember("tag = %L", 1)
.addMember("adapter = %S", "CustomStringAdapterWithALongNameThatCauses")
.build()
)
.initializer("name")
.build()
)
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter(
ParameterSpec.builder("name", String::class)
.build()
)
.build()
)
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import com.squareup.wire.WireField
|import kotlin.String
|
|public data class Taco(
| @WireField(
| tag = 1,
| adapter = "CustomStringAdapterWithALongNameThatCauses"
| )
| public val name: String
|)
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/578
@Test fun wrappingInsideKdocKeepsKdocFormatting() {
val file = FileSpec.builder("com.squareup.tacos", "Taco")
.addType(
TypeSpec.classBuilder("Builder")
.addKdoc(
"Builder class for Foo. Allows creating instances of Foo by initializing " +
"a subset of their fields, following the Builder pattern.\n"
)
.addFunction(
FunSpec.builder("summary_text")
.addKdoc(
"The description for the choice, e.g. \"Currently unavailable due to " +
"high demand. Please try later.\" May be null."
)
.addParameter("summary_text", String::class.asClassName().copy(nullable = true))
.returns(ClassName("com.squareup.tacos", "Builder"))
.addStatement("this.summary_text = summary_text")
.addStatement("return this")
.build()
)
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.tacos
|
|import kotlin.String
|
|/**
| * Builder class for Foo. Allows creating instances of Foo by initializing a subset of their fields,
| * following the Builder pattern.
| */
|public class Builder {
| /**
| * The description for the choice, e.g. "Currently unavailable due to high demand. Please try
| * later." May be null.
| */
| public fun summary_text(summary_text: String?): Builder {
| this.summary_text = summary_text
| return this
| }
|}
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/606
@Test fun typeNamesInsideTemplateStringsGetImported() {
val taco = ClassName("com.squareup.tacos", "Taco")
val file = FileSpec.builder("com.squareup.example", "Tacos")
.addFunction(
FunSpec.builder("main")
.addStatement("println(%P)", CodeBlock.of("Here's a taco: \${%T()}", taco))
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.example
|
|import com.squareup.tacos.Taco
|import kotlin.Unit
|
|public fun main(): Unit {
| println(${'"'}""Here's a taco: ${'$'}{Taco()}""${'"'})
|}
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/606
@Test fun memberNamesInsideTemplateStringsGetImported() {
val contentToString = MemberName("kotlin.collections", "contentToString")
val file = FileSpec.builder("com.squareup.example", "Tacos")
.addFunction(
FunSpec.builder("main")
.addStatement("val ints = arrayOf(1, 2, 3)")
.addStatement("println(%P)", CodeBlock.of("\${ints.%M()}", contentToString))
.build()
)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package com.squareup.example
|
|import kotlin.Unit
|import kotlin.collections.contentToString
|
|public fun main(): Unit {
| val ints = arrayOf(1, 2, 3)
| println(${'"'}""${'$'}{ints.contentToString()}""${'"'})
|}
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/701
@Test fun noIllegalCharacterInIdentifier() {
assertThrows<IllegalArgumentException> {
TypeSpec.enumBuilder("MyEnum")
.addEnumConstant("with.dots") // dots are illegal, so this should fail
.build().toString()
}.hasMessageThat().isEqualTo("Can't escape identifier `with.dots` because it contains illegal characters: .")
}
// https://github.com/square/kotlinpoet/issues/814
@Test fun percentAtTheEndOfKdoc() {
val paramSpec1 = ParameterSpec.builder("a", Int::class)
.addKdoc("Progress in %%")
.build()
val paramSpec2 = ParameterSpec.builder("b", Int::class)
.addKdoc("Some other parameter with %%")
.build()
val funSpec = FunSpec.builder("test")
.addParameters(listOf(paramSpec1, paramSpec2))
.build()
assertThat(funSpec.toString()).isEqualTo(
"""
|/**
| * @param a Progress in %
| * @param b Some other parameter with %
| */
|public fun test(a: kotlin.Int, b: kotlin.Int): kotlin.Unit {
|}
|""".trimMargin()
)
}
// https://github.com/square/kotlinpoet/issues/1031
@Test fun superClassGetsFullyQualifiedOnConflict() {
val namespace = "test"
val kotlinExceptionName = ClassName("kotlin", "Exception")
val customExceptionName = ClassName(namespace, "Exception")
val customException = TypeSpec
.classBuilder("Exception")
.superclass(kotlinExceptionName)
.addFunction(
FunSpec
.builder("test")
.addParameter("e", customExceptionName)
.build()
)
.build()
val file = FileSpec.builder(namespace, "Exception")
.addType(customException)
.build()
assertThat(file.toString()).isEqualTo(
"""
|package test
|
|import kotlin.Unit
|
|public class Exception : kotlin.Exception() {
| public fun test(e: Exception): Unit {
| }
|}
|""".trimMargin()
)
}
}