1 /* 2 * Copyright (C) 2015 Square, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.squareup.kotlinpoet 17 18 import com.google.common.collect.ImmutableMap 19 import com.google.common.truth.Truth.assertThat 20 import com.google.testing.compile.CompilationRule 21 import com.squareup.kotlinpoet.KModifier.ABSTRACT 22 import com.squareup.kotlinpoet.KModifier.DATA 23 import com.squareup.kotlinpoet.KModifier.IN 24 import com.squareup.kotlinpoet.KModifier.INNER 25 import com.squareup.kotlinpoet.KModifier.INTERNAL 26 import com.squareup.kotlinpoet.KModifier.PRIVATE 27 import com.squareup.kotlinpoet.KModifier.PUBLIC 28 import com.squareup.kotlinpoet.KModifier.SEALED 29 import com.squareup.kotlinpoet.KModifier.VARARG 30 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 31 import com.squareup.kotlinpoet.jvm.throws 32 import java.io.IOException 33 import java.io.Serializable 34 import java.lang.Deprecated 35 import java.math.BigDecimal 36 import java.util.AbstractSet 37 import java.util.Collections 38 import java.util.Comparator 39 import java.util.EventListener 40 import java.util.Locale 41 import java.util.Random 42 import java.util.concurrent.Callable 43 import java.util.function.Consumer 44 import java.util.logging.Logger 45 import javax.lang.model.element.TypeElement 46 import kotlin.reflect.KClass 47 import kotlin.reflect.KFunction 48 import kotlin.test.Ignore 49 import kotlin.test.Test 50 import kotlin.test.assertEquals 51 import kotlin.test.assertFailsWith 52 import kotlin.test.fail 53 import org.junit.Rule 54 55 @OptIn(ExperimentalKotlinPoetApi::class) 56 class TypeSpecTest { 57 private val tacosPackage = "com.squareup.tacos" 58 59 @Rule @JvmField 60 val compilation = CompilationRule() 61 getElementnull62 private fun getElement(`class`: Class<*>): TypeElement { 63 return compilation.elements.getTypeElement(`class`.canonicalName) 64 } 65 getElementnull66 private fun getElement(`class`: KClass<*>): TypeElement { 67 return getElement(`class`.java) 68 } 69 basicnull70 @Test fun basic() { 71 val taco = TypeSpec.classBuilder("Taco") 72 .addFunction( 73 FunSpec.builder("toString") 74 .addModifiers(KModifier.PUBLIC, KModifier.FINAL, KModifier.OVERRIDE) 75 .returns(String::class) 76 .addStatement("return %S", "taco") 77 .build(), 78 ) 79 .build() 80 assertThat(toString(taco)).isEqualTo( 81 """ 82 |package com.squareup.tacos 83 | 84 |import kotlin.String 85 | 86 |public class Taco { 87 | public final override fun toString(): String = "taco" 88 |} 89 | 90 """.trimMargin(), 91 ) 92 assertEquals(1906837485, taco.hashCode().toLong()) // Update expected number if source changes. 93 } 94 interestingTypesnull95 @Test fun interestingTypes() { 96 val listOfAny = List::class.asClassName().parameterizedBy(STAR) 97 val listOfExtends = List::class.asClassName() 98 .parameterizedBy(WildcardTypeName.producerOf(Serializable::class)) 99 val listOfSuper = List::class.asClassName() 100 .parameterizedBy(WildcardTypeName.consumerOf(String::class)) 101 val taco = TypeSpec.classBuilder("Taco") 102 .addProperty("star", listOfAny) 103 .addProperty("outSerializable", listOfExtends) 104 .addProperty("inString", listOfSuper) 105 .build() 106 assertThat(toString(taco)).isEqualTo( 107 """ 108 |package com.squareup.tacos 109 | 110 |import java.io.Serializable 111 |import kotlin.String 112 |import kotlin.collections.List 113 | 114 |public class Taco { 115 | public val star: List<*> 116 | 117 | public val outSerializable: List<out Serializable> 118 | 119 | public val inString: List<in String> 120 |} 121 | 122 """.trimMargin(), 123 ) 124 } 125 anonymousInnerClassnull126 @Test fun anonymousInnerClass() { 127 val foo = ClassName(tacosPackage, "Foo") 128 val bar = ClassName(tacosPackage, "Bar") 129 val thingThang = ClassName(tacosPackage, "Thing", "Thang") 130 val thingThangOfFooBar = thingThang.parameterizedBy(foo, bar) 131 val thung = ClassName(tacosPackage, "Thung") 132 val simpleThung = ClassName(tacosPackage, "SimpleThung") 133 val thungOfSuperBar = thung.parameterizedBy(WildcardTypeName.consumerOf(bar)) 134 val thungOfSuperFoo = thung.parameterizedBy(WildcardTypeName.consumerOf(foo)) 135 val simpleThungOfBar = simpleThung.parameterizedBy(bar) 136 137 val thungParameter = ParameterSpec.builder("thung", thungOfSuperFoo) 138 .build() 139 val aSimpleThung = TypeSpec.anonymousClassBuilder() 140 .superclass(simpleThungOfBar) 141 .addSuperclassConstructorParameter("%N", thungParameter) 142 .addFunction( 143 FunSpec.builder("doSomething") 144 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 145 .addParameter("bar", bar) 146 .addCode("/* code snippets */\n") 147 .build(), 148 ) 149 .build() 150 val aThingThang = TypeSpec.anonymousClassBuilder() 151 .superclass(thingThangOfFooBar) 152 .addFunction( 153 FunSpec.builder("call") 154 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 155 .returns(thungOfSuperBar) 156 .addParameter(thungParameter) 157 .addStatement("return %L", aSimpleThung) 158 .build(), 159 ) 160 .build() 161 val taco = TypeSpec.classBuilder("Taco") 162 .addProperty( 163 PropertySpec.builder("NAME", thingThangOfFooBar) 164 .initializer("%L", aThingThang) 165 .build(), 166 ) 167 .build() 168 169 assertThat(toString(taco)).isEqualTo( 170 """ 171 |package com.squareup.tacos 172 | 173 |public class Taco { 174 | public val NAME: Thing.Thang<Foo, Bar> = object : Thing.Thang<Foo, Bar>() { 175 | public override fun call(thung: Thung<in Foo>): Thung<in Bar> = object : SimpleThung<Bar>(thung) 176 | { 177 | public override fun doSomething(bar: Bar) { 178 | /* code snippets */ 179 | } 180 | } 181 | } 182 |} 183 | 184 """.trimMargin(), 185 ) 186 } 187 anonymousClassWithSuperClassConstructorCallnull188 @Test fun anonymousClassWithSuperClassConstructorCall() { 189 val superclass = ArrayList::class.parameterizedBy(String::class) 190 val anonymousClass = TypeSpec.anonymousClassBuilder() 191 .addSuperclassConstructorParameter("%L", "4") 192 .superclass(superclass) 193 .build() 194 val taco = TypeSpec.classBuilder("Taco") 195 .addProperty( 196 PropertySpec.builder("names", superclass) 197 .initializer("%L", anonymousClass) 198 .build(), 199 ).build() 200 201 assertThat(toString(taco)).isEqualTo( 202 """ 203 |package com.squareup.tacos 204 | 205 |import java.util.ArrayList 206 |import kotlin.String 207 | 208 |public class Taco { 209 | public val names: ArrayList<String> = object : ArrayList<String>(4) { 210 | } 211 |} 212 | 213 """.trimMargin(), 214 ) 215 } 216 217 // https://github.com/square/kotlinpoet/issues/315 anonymousClassWithMultipleSuperTypesnull218 @Test fun anonymousClassWithMultipleSuperTypes() { 219 val superclass = ClassName("com.squareup.wire", "Message") 220 val anonymousClass = TypeSpec.anonymousClassBuilder() 221 .superclass(superclass) 222 .addSuperinterface(Runnable::class) 223 .addFunction( 224 FunSpec.builder("run") 225 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 226 .addCode("/* code snippets */\n") 227 .build(), 228 ).build() 229 val taco = TypeSpec.classBuilder("Taco") 230 .addProperty( 231 PropertySpec.builder("NAME", Runnable::class) 232 .initializer("%L", anonymousClass) 233 .build(), 234 ).build() 235 236 assertThat(toString(taco)).isEqualTo( 237 """ 238 |package com.squareup.tacos 239 | 240 |import com.squareup.wire.Message 241 |import java.lang.Runnable 242 | 243 |public class Taco { 244 | public val NAME: Runnable = object : Message(), Runnable { 245 | public override fun run() { 246 | /* code snippets */ 247 | } 248 | } 249 |} 250 | 251 """.trimMargin(), 252 ) 253 } 254 anonymousClassWithoutSuperTypenull255 @Test fun anonymousClassWithoutSuperType() { 256 val anonymousClass = TypeSpec.anonymousClassBuilder().build() 257 val taco = TypeSpec.classBuilder("Taco") 258 .addProperty( 259 PropertySpec.builder("NAME", Any::class) 260 .initializer("%L", anonymousClass) 261 .build(), 262 ).build() 263 264 assertThat(toString(taco)).isEqualTo( 265 """ 266 |package com.squareup.tacos 267 | 268 |import kotlin.Any 269 | 270 |public class Taco { 271 | public val NAME: Any = object { 272 | } 273 |} 274 | 275 """.trimMargin(), 276 ) 277 } 278 annotatedParametersnull279 @Test fun annotatedParameters() { 280 val service = TypeSpec.classBuilder("Foo") 281 .addFunction( 282 FunSpec.constructorBuilder() 283 .addModifiers(KModifier.PUBLIC) 284 .addParameter("id", Long::class) 285 .addParameter( 286 ParameterSpec.builder("one", String::class) 287 .addAnnotation(ClassName(tacosPackage, "Ping")) 288 .build(), 289 ) 290 .addParameter( 291 ParameterSpec.builder("two", String::class) 292 .addAnnotation(ClassName(tacosPackage, "Ping")) 293 .build(), 294 ) 295 .addParameter( 296 ParameterSpec.builder("three", String::class) 297 .addAnnotation( 298 AnnotationSpec.builder(ClassName(tacosPackage, "Pong")) 299 .addMember("%S", "pong") 300 .build(), 301 ) 302 .build(), 303 ) 304 .addParameter( 305 ParameterSpec.builder("four", String::class) 306 .addAnnotation(ClassName(tacosPackage, "Ping")) 307 .build(), 308 ) 309 .addCode("/* code snippets */\n") 310 .build(), 311 ) 312 .build() 313 314 assertThat(toString(service)).isEqualTo( 315 """ 316 |package com.squareup.tacos 317 | 318 |import kotlin.Long 319 |import kotlin.String 320 | 321 |public class Foo { 322 | public constructor( 323 | id: Long, 324 | @Ping one: String, 325 | @Ping two: String, 326 | @Pong("pong") three: String, 327 | @Ping four: String, 328 | ) { 329 | /* code snippets */ 330 | } 331 |} 332 | 333 """.trimMargin(), 334 ) 335 } 336 337 /** 338 * We had a bug where annotations were preventing us from doing the right thing when resolving 339 * imports. https://github.com/square/javapoet/issues/422 340 */ annotationsAndJavaLangTypesnull341 @Test fun annotationsAndJavaLangTypes() { 342 val freeRange = ClassName("javax.annotation", "FreeRange") 343 val taco = TypeSpec.classBuilder("EthicalTaco") 344 .addProperty( 345 "meat", 346 String::class.asClassName() 347 .copy(annotations = listOf(AnnotationSpec.builder(freeRange).build())), 348 ) 349 .build() 350 351 assertThat(toString(taco)).isEqualTo( 352 """ 353 |package com.squareup.tacos 354 | 355 |import javax.`annotation`.FreeRange 356 |import kotlin.String 357 | 358 |public class EthicalTaco { 359 | public val meat: @FreeRange String 360 |} 361 | 362 """.trimMargin(), 363 ) 364 } 365 retrofitStyleInterfacenull366 @Test fun retrofitStyleInterface() { 367 val observable = ClassName(tacosPackage, "Observable") 368 val fooBar = ClassName(tacosPackage, "FooBar") 369 val thing = ClassName(tacosPackage, "Thing") 370 val things = ClassName(tacosPackage, "Things") 371 val map = Map::class.asClassName() 372 val string = String::class.asClassName() 373 val headers = ClassName(tacosPackage, "Headers") 374 val post = ClassName(tacosPackage, "POST") 375 val body = ClassName(tacosPackage, "Body") 376 val queryMap = ClassName(tacosPackage, "QueryMap") 377 val header = ClassName(tacosPackage, "Header") 378 val service = TypeSpec.interfaceBuilder("Service") 379 .addFunction( 380 FunSpec.builder("fooBar") 381 .addModifiers(KModifier.PUBLIC, KModifier.ABSTRACT) 382 .addAnnotation( 383 AnnotationSpec.builder(headers) 384 .addMember("%S", "Accept: application/json") 385 .addMember("%S", "User-Agent: foobar") 386 .build(), 387 ) 388 .addAnnotation( 389 AnnotationSpec.builder(post) 390 .addMember("%S", "/foo/bar") 391 .build(), 392 ) 393 .returns(observable.parameterizedBy(fooBar)) 394 .addParameter( 395 ParameterSpec.builder("things", things.parameterizedBy(thing)) 396 .addAnnotation(body) 397 .build(), 398 ) 399 .addParameter( 400 ParameterSpec.builder("query", map.parameterizedBy(string, string)) 401 .addAnnotation( 402 AnnotationSpec.builder(queryMap) 403 .addMember("encodeValues = %L", "false") 404 .build(), 405 ) 406 .build(), 407 ) 408 .addParameter( 409 ParameterSpec.builder("authorization", string) 410 .addAnnotation( 411 AnnotationSpec.builder(header) 412 .addMember("%S", "Authorization") 413 .build(), 414 ) 415 .build(), 416 ) 417 .build(), 418 ) 419 .build() 420 421 assertThat(toString(service)).isEqualTo( 422 """ 423 |package com.squareup.tacos 424 | 425 |import kotlin.String 426 |import kotlin.collections.Map 427 | 428 |public interface Service { 429 | @Headers( 430 | "Accept: application/json", 431 | "User-Agent: foobar", 432 | ) 433 | @POST("/foo/bar") 434 | public fun fooBar( 435 | @Body things: Things<Thing>, 436 | @QueryMap(encodeValues = false) query: Map<String, String>, 437 | @Header("Authorization") authorization: String, 438 | ): Observable<FooBar> 439 |} 440 | 441 """.trimMargin(), 442 ) 443 } 444 annotatedPropertynull445 @Test fun annotatedProperty() { 446 val taco = TypeSpec.classBuilder("Taco") 447 .addProperty( 448 PropertySpec.builder("thing", String::class, KModifier.PRIVATE) 449 .addAnnotation( 450 AnnotationSpec.builder(ClassName(tacosPackage, "JsonAdapter")) 451 .addMember("%T::class", ClassName(tacosPackage, "Foo")) 452 .build(), 453 ) 454 .build(), 455 ) 456 .build() 457 assertThat(toString(taco)).isEqualTo( 458 """ 459 |package com.squareup.tacos 460 | 461 |import kotlin.String 462 | 463 |public class Taco { 464 | @JsonAdapter(Foo::class) 465 | private val thing: String 466 |} 467 | 468 """.trimMargin(), 469 ) 470 } 471 annotatedPropertyUseSiteTargetnull472 @Test fun annotatedPropertyUseSiteTarget() { 473 val taco = TypeSpec.classBuilder("Taco") 474 .addProperty( 475 PropertySpec.builder("thing", String::class, KModifier.PRIVATE) 476 .addAnnotation( 477 AnnotationSpec.builder(ClassName(tacosPackage, "JsonAdapter")) 478 .addMember("%T::class", ClassName(tacosPackage, "Foo")) 479 .useSiteTarget(AnnotationSpec.UseSiteTarget.FIELD) 480 .build(), 481 ) 482 .build(), 483 ) 484 .build() 485 assertThat(toString(taco)).isEqualTo( 486 """ 487 |package com.squareup.tacos 488 | 489 |import kotlin.String 490 | 491 |public class Taco { 492 | @field:JsonAdapter(Foo::class) 493 | private val thing: String 494 |} 495 | 496 """.trimMargin(), 497 ) 498 } 499 annotatedClassnull500 @Test fun annotatedClass() { 501 val someType = ClassName(tacosPackage, "SomeType") 502 val taco = TypeSpec.classBuilder("Foo") 503 .addAnnotation( 504 AnnotationSpec.builder(ClassName(tacosPackage, "Something")) 505 .addMember("%T.%N", someType, "PROPERTY") 506 .addMember("%L", 12) 507 .addMember("%S", "goodbye") 508 .build(), 509 ) 510 .addModifiers(KModifier.PUBLIC) 511 .build() 512 assertThat(toString(taco)).isEqualTo( 513 """ 514 |package com.squareup.tacos 515 | 516 |@Something( 517 | SomeType.PROPERTY, 518 | 12, 519 | "goodbye", 520 |) 521 |public class Foo 522 | 523 """.trimMargin(), 524 ) 525 } 526 enumWithSubclassingnull527 @Test fun enumWithSubclassing() { 528 val roshambo = TypeSpec.enumBuilder("Roshambo") 529 .addModifiers(KModifier.PUBLIC) 530 .addEnumConstant( 531 "ROCK", 532 TypeSpec.anonymousClassBuilder() 533 .addKdoc("Avalanche!\n") 534 .build(), 535 ) 536 .addEnumConstant( 537 "PAPER", 538 TypeSpec.anonymousClassBuilder() 539 .addSuperclassConstructorParameter("%S", "flat") 540 .addFunction( 541 FunSpec.builder("toString") 542 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE, KModifier.OVERRIDE) 543 .returns(String::class) 544 .addCode("return %S\n", "paper airplane!") 545 .build(), 546 ) 547 .build(), 548 ) 549 .addEnumConstant( 550 "SCISSORS", 551 TypeSpec.anonymousClassBuilder() 552 .addSuperclassConstructorParameter("%S", "peace sign") 553 .build(), 554 ) 555 .addProperty( 556 PropertySpec.builder("handPosition", String::class, KModifier.PRIVATE) 557 .initializer("handPosition") 558 .build(), 559 ) 560 .primaryConstructor( 561 FunSpec.constructorBuilder() 562 .addParameter("handPosition", String::class) 563 .build(), 564 ) 565 .addFunction( 566 FunSpec.constructorBuilder() 567 .addCode("this(%S)\n", "fist") 568 .build(), 569 ) 570 .build() 571 assertThat(toString(roshambo)).isEqualTo( 572 """ 573 |package com.squareup.tacos 574 | 575 |import kotlin.String 576 | 577 |public enum class Roshambo( 578 | private val handPosition: String, 579 |) { 580 | /** 581 | * Avalanche! 582 | */ 583 | ROCK, 584 | PAPER("flat") { 585 | public override fun toString(): String = "paper airplane!" 586 | }, 587 | SCISSORS("peace sign"), 588 | ; 589 | 590 | public constructor() { 591 | this("fist") 592 | } 593 |} 594 | 595 """.trimMargin(), 596 ) 597 } 598 enumWithPrimaryConstructorAndMultipleInterfacesnull599 @Test fun enumWithPrimaryConstructorAndMultipleInterfaces() { 600 val roshambo = TypeSpec.enumBuilder("Roshambo") 601 .addSuperinterface(Runnable::class) 602 .addSuperinterface(Cloneable::class) 603 .addEnumConstant( 604 "SCISSORS", 605 TypeSpec.anonymousClassBuilder() 606 .addSuperclassConstructorParameter("%S", "peace sign") 607 .build(), 608 ) 609 .addProperty( 610 PropertySpec.builder("handPosition", String::class, KModifier.PRIVATE) 611 .initializer("handPosition") 612 .build(), 613 ) 614 .primaryConstructor( 615 FunSpec.constructorBuilder() 616 .addParameter("handPosition", String::class) 617 .build(), 618 ) 619 .build() 620 assertThat(toString(roshambo)).isEqualTo( 621 """ 622 |package com.squareup.tacos 623 | 624 |import java.lang.Runnable 625 |import kotlin.Cloneable 626 |import kotlin.String 627 | 628 |public enum class Roshambo( 629 | private val handPosition: String, 630 |) : Runnable, 631 | Cloneable { 632 | SCISSORS("peace sign"), 633 | ; 634 |} 635 | 636 """.trimMargin(), 637 ) 638 } 639 640 /** https://github.com/square/javapoet/issues/193 */ enumsMayDefineAbstractFunctionsnull641 @Test fun enumsMayDefineAbstractFunctions() { 642 val roshambo = TypeSpec.enumBuilder("Tortilla") 643 .addModifiers(KModifier.PUBLIC) 644 .addEnumConstant( 645 "CORN", 646 TypeSpec.anonymousClassBuilder() 647 .addFunction( 648 FunSpec.builder("fold") 649 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 650 .build(), 651 ) 652 .build(), 653 ) 654 .addFunction( 655 FunSpec.builder("fold") 656 .addModifiers(KModifier.PUBLIC, KModifier.ABSTRACT) 657 .build(), 658 ) 659 .build() 660 assertThat(toString(roshambo)).isEqualTo( 661 """ 662 |package com.squareup.tacos 663 | 664 |public enum class Tortilla { 665 | CORN { 666 | public override fun fold() { 667 | } 668 | }, 669 | ; 670 | 671 | public abstract fun fold() 672 |} 673 | 674 """.trimMargin(), 675 ) 676 } 677 enumsMayHavePrivateConstructorValsnull678 @Test fun enumsMayHavePrivateConstructorVals() { 679 val enum = TypeSpec.enumBuilder("MyEnum") 680 .primaryConstructor( 681 FunSpec.constructorBuilder().addParameter("number", Int::class).build(), 682 ) 683 .addProperty( 684 PropertySpec.builder("number", Int::class) 685 .addModifiers(PRIVATE) 686 .initializer("number") 687 .build(), 688 ) 689 .build() 690 assertThat(toString(enum)).isEqualTo( 691 """ 692 |package com.squareup.tacos 693 | 694 |import kotlin.Int 695 | 696 |public enum class MyEnum( 697 | private val number: Int, 698 |) 699 | 700 """.trimMargin(), 701 ) 702 } 703 classesMayHavePrivateConstructorPropertiesInTheirPrimaryConstructorsnull704 @Test fun classesMayHavePrivateConstructorPropertiesInTheirPrimaryConstructors() { 705 val myClass = TypeSpec.classBuilder("MyClass") 706 .primaryConstructor( 707 FunSpec.constructorBuilder().addParameter("number", Int::class).build(), 708 ) 709 .addProperty( 710 PropertySpec.builder("number", Int::class) 711 .initializer("number") 712 .addModifiers(PRIVATE) 713 .build(), 714 ) 715 .build() 716 assertThat(toString(myClass)).isEqualTo( 717 """ 718 |package com.squareup.tacos 719 | 720 |import kotlin.Int 721 | 722 |public class MyClass( 723 | private val number: Int, 724 |) 725 | 726 """.trimMargin(), 727 ) 728 } 729 sealedClassesMayDefineAbstractMembersnull730 @Test fun sealedClassesMayDefineAbstractMembers() { 731 val sealedClass = TypeSpec.classBuilder("Sealed") 732 .addModifiers(KModifier.SEALED) 733 .addProperty(PropertySpec.builder("name", String::class).addModifiers(ABSTRACT).build()) 734 .addFunction(FunSpec.builder("fold").addModifiers(KModifier.PUBLIC, KModifier.ABSTRACT).build()) 735 .build() 736 assertThat(toString(sealedClass)).isEqualTo( 737 """ 738 |package com.squareup.tacos 739 | 740 |import kotlin.String 741 | 742 |public sealed class Sealed { 743 | public abstract val name: String 744 | 745 | public abstract fun fold() 746 |} 747 | 748 """.trimMargin(), 749 ) 750 } 751 classesMayHaveVarargConstructorPropertiesnull752 @Test fun classesMayHaveVarargConstructorProperties() { 753 val variable = TypeSpec.classBuilder("Variable") 754 .primaryConstructor( 755 FunSpec.constructorBuilder() 756 .addParameter(ParameterSpec.builder("name", String::class, VARARG).build()) 757 .build(), 758 ) 759 .addProperty(PropertySpec.builder("name", String::class).initializer("name").build()) 760 .build() 761 assertThat(toString(variable)).isEqualTo( 762 """ 763 |package com.squareup.tacos 764 | 765 |import kotlin.String 766 | 767 |public class Variable( 768 | public vararg val name: String, 769 |) 770 | 771 """.trimMargin(), 772 ) 773 } 774 775 /** https://github.com/square/kotlinpoet/issues/942 */ noConstructorPropertiesWithCustomGetternull776 @Test fun noConstructorPropertiesWithCustomGetter() { 777 val taco = TypeSpec.classBuilder("ObservantTaco") 778 .primaryConstructor( 779 FunSpec.constructorBuilder() 780 .addParameter(ParameterSpec.builder("contents", String::class).build()) 781 .build(), 782 ) 783 .addProperty( 784 PropertySpec.builder("contents", String::class).initializer("contents") 785 .getter(FunSpec.getterBuilder().addCode("println(%S)\nreturn field", "contents observed!").build()) 786 .build(), 787 ) 788 .build() 789 assertThat(toString(taco)).isEqualTo( 790 """ 791 |package com.squareup.tacos 792 | 793 |import kotlin.String 794 | 795 |public class ObservantTaco( 796 | contents: String, 797 |) { 798 | public val contents: String = contents 799 | get() { 800 | println("contents observed!") 801 | return field 802 | } 803 |} 804 | 805 """.trimMargin(), 806 ) 807 } 808 noConstructorPropertiesWithCustomSetternull809 @Test fun noConstructorPropertiesWithCustomSetter() { 810 val taco = TypeSpec.classBuilder("ObservantTaco") 811 .primaryConstructor( 812 FunSpec.constructorBuilder() 813 .addParameter(ParameterSpec.builder("contents", String::class).build()) 814 .build(), 815 ) 816 .addProperty( 817 PropertySpec.builder("contents", String::class).initializer("contents") 818 .mutable() 819 .setter( 820 FunSpec.setterBuilder() 821 .addParameter("value", String::class) 822 .addCode("println(%S)\nfield = value", "contents changed!").build(), 823 ) 824 .build(), 825 ) 826 .build() 827 assertThat(toString(taco)).isEqualTo( 828 """ 829 |package com.squareup.tacos 830 | 831 |import kotlin.String 832 | 833 |public class ObservantTaco( 834 | contents: String, 835 |) { 836 | public var contents: String = contents 837 | set(`value`) { 838 | println("contents changed!") 839 | field = value 840 | } 841 |} 842 | 843 """.trimMargin(), 844 ) 845 } 846 onlyEnumsMayHaveEnumConstantsnull847 @Test fun onlyEnumsMayHaveEnumConstants() { 848 assertThrows<IllegalStateException> { 849 TypeSpec.classBuilder("Roshambo") 850 .addEnumConstant("ROCK") 851 .build() 852 } 853 } 854 855 /** https://github.com/square/kotlinpoet/issues/621 */ enumWithMembersButNoConstanstsnull856 @Test fun enumWithMembersButNoConstansts() { 857 val roshambo = TypeSpec.enumBuilder("RenderPassCreate") 858 .addType(TypeSpec.companionObjectBuilder().build()) 859 .build() 860 assertThat(toString(roshambo)).isEqualTo( 861 """ 862 |package com.squareup.tacos 863 | 864 |public enum class RenderPassCreate { 865 | ; 866 | public companion object 867 |} 868 | 869 """.trimMargin(), 870 ) 871 } 872 enumWithMembersButNoConstructorCallnull873 @Test fun enumWithMembersButNoConstructorCall() { 874 val roshambo = TypeSpec.enumBuilder("Roshambo") 875 .addEnumConstant( 876 "SPOCK", 877 TypeSpec.anonymousClassBuilder() 878 .addFunction( 879 FunSpec.builder("toString") 880 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 881 .returns(String::class) 882 .addStatement("return %S", "west side") 883 .build(), 884 ) 885 .build(), 886 ) 887 .build() 888 assertThat(toString(roshambo)).isEqualTo( 889 """ 890 |package com.squareup.tacos 891 | 892 |import kotlin.String 893 | 894 |public enum class Roshambo { 895 | SPOCK { 896 | public override fun toString(): String = "west side" 897 | }, 898 |} 899 | 900 """.trimMargin(), 901 ) 902 } 903 904 /** https://github.com/square/javapoet/issues/253 */ enumWithAnnotatedValuesnull905 @Test fun enumWithAnnotatedValues() { 906 val roshambo = TypeSpec.enumBuilder("Roshambo") 907 .addModifiers(KModifier.PUBLIC) 908 .addEnumConstant( 909 "ROCK", 910 TypeSpec.anonymousClassBuilder() 911 .addAnnotation(java.lang.Deprecated::class) 912 .build(), 913 ) 914 .addEnumConstant("PAPER") 915 .addEnumConstant("SCISSORS") 916 .build() 917 assertThat(toString(roshambo)).isEqualTo( 918 """ 919 |package com.squareup.tacos 920 | 921 |import java.lang.Deprecated 922 | 923 |public enum class Roshambo { 924 | @Deprecated 925 | ROCK, 926 | PAPER, 927 | SCISSORS, 928 |} 929 | 930 """.trimMargin(), 931 ) 932 } 933 funThrowsnull934 @Test fun funThrows() { 935 val taco = TypeSpec.classBuilder("Taco") 936 .addModifiers(KModifier.ABSTRACT) 937 .addFunction( 938 FunSpec.builder("throwOne") 939 .throws(IOException::class) 940 .build(), 941 ) 942 .addFunction( 943 FunSpec.builder("throwTwo") 944 .throws(IOException::class.asClassName(), ClassName(tacosPackage, "SourCreamException")) 945 .build(), 946 ) 947 .addFunction( 948 FunSpec.builder("abstractThrow") 949 .addModifiers(KModifier.ABSTRACT) 950 .throws(IOException::class) 951 .build(), 952 ) 953 .addFunction( 954 FunSpec.builder("nativeThrow") 955 .addModifiers(KModifier.EXTERNAL) 956 .throws(IOException::class) 957 .build(), 958 ) 959 .build() 960 assertThat(toString(taco)).isEqualTo( 961 """ 962 |package com.squareup.tacos 963 | 964 |import java.io.IOException 965 |import kotlin.jvm.Throws 966 | 967 |public abstract class Taco { 968 | @Throws(IOException::class) 969 | public fun throwOne() { 970 | } 971 | 972 | @Throws( 973 | IOException::class, 974 | SourCreamException::class, 975 | ) 976 | public fun throwTwo() { 977 | } 978 | 979 | @Throws(IOException::class) 980 | public abstract fun abstractThrow() 981 | 982 | @Throws(IOException::class) 983 | public external fun nativeThrow() 984 |} 985 | 986 """.trimMargin(), 987 ) 988 } 989 typeVariablesnull990 @Test fun typeVariables() { 991 val t = TypeVariableName("T") 992 val p = TypeVariableName("P", Number::class) 993 val location = ClassName(tacosPackage, "Location") 994 val typeSpec = TypeSpec.classBuilder("Location") 995 .addTypeVariable(t) 996 .addTypeVariable(p) 997 .addSuperinterface(Comparable::class.asClassName().parameterizedBy(p)) 998 .addProperty("label", t) 999 .addProperty("x", p) 1000 .addProperty("y", p) 1001 .addFunction( 1002 FunSpec.builder("compareTo") 1003 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 1004 .returns(Int::class) 1005 .addParameter("p", p) 1006 .addStatement("return 0") 1007 .build(), 1008 ) 1009 .addFunction( 1010 FunSpec.builder("of") 1011 .addModifiers(KModifier.PUBLIC) 1012 .addTypeVariable(t) 1013 .addTypeVariable(p) 1014 .returns(location.parameterizedBy(t, p)) 1015 .addParameter("label", t) 1016 .addParameter("x", p) 1017 .addParameter("y", p) 1018 .addStatement("throw %T(%S)", UnsupportedOperationException::class, "TODO") 1019 .build(), 1020 ) 1021 .build() 1022 assertThat(toString(typeSpec)).isEqualTo( 1023 """ 1024 |package com.squareup.tacos 1025 | 1026 |import java.lang.UnsupportedOperationException 1027 |import kotlin.Comparable 1028 |import kotlin.Int 1029 |import kotlin.Number 1030 | 1031 |public class Location<T, P : Number> : Comparable<P> { 1032 | public val label: T 1033 | 1034 | public val x: P 1035 | 1036 | public val y: P 1037 | 1038 | public override fun compareTo(p: P): Int = 0 1039 | 1040 | public fun <T, P : Number> of( 1041 | label: T, 1042 | x: P, 1043 | y: P, 1044 | ): Location<T, P> = throw UnsupportedOperationException("TODO") 1045 |} 1046 | 1047 """.trimMargin(), 1048 ) 1049 } 1050 typeVariableWithBoundsnull1051 @Test fun typeVariableWithBounds() { 1052 val a = AnnotationSpec.builder(ClassName("com.squareup.tacos", "A")).build() 1053 val p = TypeVariableName("P", Number::class) 1054 val q = TypeVariableName("Q", Number::class).copy(annotations = listOf(a)) as TypeVariableName 1055 val typeSpec = TypeSpec.classBuilder("Location") 1056 .addTypeVariable(p.copy(bounds = p.bounds + listOf(Comparable::class.asTypeName()))) 1057 .addTypeVariable(q.copy(bounds = q.bounds + listOf(Comparable::class.asTypeName()))) 1058 .addProperty("x", p) 1059 .addProperty("y", q) 1060 .primaryConstructor(FunSpec.constructorBuilder().build()) 1061 .superclass(Number::class) 1062 .addSuperinterface(Comparable::class) 1063 .build() 1064 assertThat(toString(typeSpec)).isEqualTo( 1065 """ 1066 |package com.squareup.tacos 1067 | 1068 |import kotlin.Comparable 1069 |import kotlin.Number 1070 | 1071 |public class Location<P, Q>() : Number(), Comparable where P : Number, P : Comparable, Q : Number, Q 1072 | : Comparable { 1073 | public val x: P 1074 | 1075 | public val y: @A Q 1076 |} 1077 | 1078 """.trimMargin(), 1079 ) 1080 } 1081 classImplementsExtendsnull1082 @Test fun classImplementsExtends() { 1083 val taco = ClassName(tacosPackage, "Taco") 1084 val food = ClassName("com.squareup.tacos", "Food") 1085 val typeSpec = TypeSpec.classBuilder("Taco") 1086 .addModifiers(KModifier.ABSTRACT) 1087 .superclass(AbstractSet::class.asClassName().parameterizedBy(food)) 1088 .addSuperinterface(Serializable::class) 1089 .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco)) 1090 .build() 1091 assertThat(toString(typeSpec)).isEqualTo( 1092 """ 1093 |package com.squareup.tacos 1094 | 1095 |import java.io.Serializable 1096 |import java.util.AbstractSet 1097 |import kotlin.Comparable 1098 | 1099 |public abstract class Taco : AbstractSet<Food>(), Serializable, Comparable<Taco> 1100 | 1101 """.trimMargin(), 1102 ) 1103 } 1104 classImplementsExtendsPrimaryConstructorNoParamsnull1105 @Test fun classImplementsExtendsPrimaryConstructorNoParams() { 1106 val taco = ClassName(tacosPackage, "Taco") 1107 val food = ClassName("com.squareup.tacos", "Food") 1108 val typeSpec = TypeSpec.classBuilder("Taco") 1109 .addModifiers(ABSTRACT) 1110 .superclass(AbstractSet::class.asClassName().parameterizedBy(food)) 1111 .addSuperinterface(Serializable::class) 1112 .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco)) 1113 .primaryConstructor(FunSpec.constructorBuilder().build()) 1114 .build() 1115 assertThat(toString(typeSpec)).isEqualTo( 1116 """ 1117 |package com.squareup.tacos 1118 | 1119 |import java.io.Serializable 1120 |import java.util.AbstractSet 1121 |import kotlin.Comparable 1122 | 1123 |public abstract class Taco() : AbstractSet<Food>(), Serializable, Comparable<Taco> 1124 | 1125 """.trimMargin(), 1126 ) 1127 } 1128 classImplementsExtendsPrimaryConstructorWithParamsnull1129 @Test fun classImplementsExtendsPrimaryConstructorWithParams() { 1130 val taco = ClassName(tacosPackage, "Taco") 1131 val food = ClassName("com.squareup.tacos", "Food") 1132 val typeSpec = TypeSpec.classBuilder("Taco") 1133 .addModifiers(ABSTRACT) 1134 .superclass(AbstractSet::class.asClassName().parameterizedBy(food)) 1135 .addSuperinterface(Serializable::class) 1136 .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco)) 1137 .primaryConstructor( 1138 FunSpec.constructorBuilder() 1139 .addParameter("name", String::class) 1140 .build(), 1141 ) 1142 .build() 1143 assertThat(toString(typeSpec)).isEqualTo( 1144 """ 1145 |package com.squareup.tacos 1146 | 1147 |import java.io.Serializable 1148 |import java.util.AbstractSet 1149 |import kotlin.Comparable 1150 |import kotlin.String 1151 | 1152 |public abstract class Taco( 1153 | name: String, 1154 |) : AbstractSet<Food>(), 1155 | Serializable, 1156 | Comparable<Taco> 1157 | 1158 """.trimMargin(), 1159 ) 1160 } 1161 classImplementsExtendsSameNamenull1162 @Test fun classImplementsExtendsSameName() { 1163 val javapoetTaco = ClassName(tacosPackage, "Taco") 1164 val tacoBellTaco = ClassName("com.taco.bell", "Taco") 1165 val fishTaco = ClassName("org.fish.taco", "Taco") 1166 val typeSpec = TypeSpec.classBuilder("Taco") 1167 .superclass(fishTaco) 1168 .addSuperinterface(Comparable::class.asClassName().parameterizedBy(javapoetTaco)) 1169 .addSuperinterface(tacoBellTaco) 1170 .build() 1171 assertThat(toString(typeSpec)).isEqualTo( 1172 """ 1173 |package com.squareup.tacos 1174 | 1175 |import kotlin.Comparable 1176 | 1177 |public class Taco : org.fish.taco.Taco(), Comparable<Taco>, com.taco.bell.Taco 1178 | 1179 """.trimMargin(), 1180 ) 1181 } 1182 classImplementsInnerClassnull1183 @Test fun classImplementsInnerClass() { 1184 val outer = ClassName(tacosPackage, "Outer") 1185 val inner = outer.nestedClass("Inner") 1186 val callable = Callable::class.asClassName() 1187 val typeSpec = TypeSpec.classBuilder("Outer") 1188 .superclass(callable.parameterizedBy(inner)) 1189 .addType( 1190 TypeSpec.classBuilder("Inner") 1191 .addModifiers(KModifier.INNER) 1192 .build(), 1193 ) 1194 .build() 1195 1196 assertThat(toString(typeSpec)).isEqualTo( 1197 """ 1198 |package com.squareup.tacos 1199 | 1200 |import java.util.concurrent.Callable 1201 | 1202 |public class Outer : Callable<Outer.Inner>() { 1203 | public inner class Inner 1204 |} 1205 | 1206 """.trimMargin(), 1207 ) 1208 } 1209 enumImplementsnull1210 @Test fun enumImplements() { 1211 val typeSpec = TypeSpec.enumBuilder("Food") 1212 .addSuperinterface(Serializable::class) 1213 .addSuperinterface(Cloneable::class) 1214 .addEnumConstant("LEAN_GROUND_BEEF") 1215 .addEnumConstant("SHREDDED_CHEESE") 1216 .build() 1217 assertThat(toString(typeSpec)).isEqualTo( 1218 """ 1219 |package com.squareup.tacos 1220 | 1221 |import java.io.Serializable 1222 |import kotlin.Cloneable 1223 | 1224 |public enum class Food : Serializable, Cloneable { 1225 | LEAN_GROUND_BEEF, 1226 | SHREDDED_CHEESE, 1227 |} 1228 | 1229 """.trimMargin(), 1230 ) 1231 } 1232 enumWithConstructorsAndKeywordsnull1233 @Test fun enumWithConstructorsAndKeywords() { 1234 val primaryConstructor = FunSpec.constructorBuilder() 1235 .addParameter("value", Int::class) 1236 .build() 1237 val typeSpec = TypeSpec.enumBuilder("Sort") 1238 .primaryConstructor(primaryConstructor) 1239 .addEnumConstant( 1240 "open", 1241 TypeSpec.anonymousClassBuilder() 1242 .addSuperclassConstructorParameter("%L", 0) 1243 .build(), 1244 ) 1245 .addEnumConstant( 1246 "closed", 1247 TypeSpec.anonymousClassBuilder() 1248 .addSuperclassConstructorParameter("%L", 1) 1249 .build(), 1250 ) 1251 .build() 1252 assertThat(toString(typeSpec)).isEqualTo( 1253 """ 1254 |package com.squareup.tacos 1255 | 1256 |import kotlin.Int 1257 | 1258 |public enum class Sort( 1259 | `value`: Int, 1260 |) { 1261 | `open`(0), 1262 | closed(1), 1263 |} 1264 | 1265 """.trimMargin(), 1266 ) 1267 } 1268 interfaceExtendsnull1269 @Test fun interfaceExtends() { 1270 val taco = ClassName(tacosPackage, "Taco") 1271 val typeSpec = TypeSpec.interfaceBuilder("Taco") 1272 .addSuperinterface(Serializable::class) 1273 .addSuperinterface(Comparable::class.asClassName().parameterizedBy(taco)) 1274 .build() 1275 assertThat(toString(typeSpec)).isEqualTo( 1276 """ 1277 |package com.squareup.tacos 1278 | 1279 |import java.io.Serializable 1280 |import kotlin.Comparable 1281 | 1282 |public interface Taco : Serializable, Comparable<Taco> 1283 | 1284 """.trimMargin(), 1285 ) 1286 } 1287 funInterfacenull1288 @Test fun funInterface() { 1289 val taco = ClassName(tacosPackage, "Taco") 1290 val typeSpec = TypeSpec.funInterfaceBuilder(taco) 1291 .addFunction( 1292 FunSpec.builder("sam") 1293 .addModifiers(ABSTRACT) 1294 .build(), 1295 ) 1296 .addFunction(FunSpec.builder("notSam").build()) 1297 .build() 1298 assertThat(typeSpec.isFunctionalInterface).isTrue() 1299 assertThat(toString(typeSpec)).isEqualTo( 1300 """ 1301 |package com.squareup.tacos 1302 | 1303 |public fun interface Taco { 1304 | public fun sam() 1305 | 1306 | public fun notSam() { 1307 | } 1308 |} 1309 | 1310 """.trimMargin(), 1311 ) 1312 } 1313 funInterface_empty_shouldErrornull1314 @Test fun funInterface_empty_shouldError() { 1315 assertThrows<IllegalStateException> { 1316 TypeSpec.funInterfaceBuilder("Taco") 1317 .build() 1318 }.hasMessageThat() 1319 .contains("Functional interfaces must have exactly one abstract function. Contained 0") 1320 } 1321 funInterface_multipleAbstract_shouldErrornull1322 @Test fun funInterface_multipleAbstract_shouldError() { 1323 assertThrows<IllegalStateException> { 1324 TypeSpec.funInterfaceBuilder("Taco") 1325 .addFunction( 1326 FunSpec.builder("fun1") 1327 .addModifiers(ABSTRACT) 1328 .build(), 1329 ) 1330 .addFunction( 1331 FunSpec.builder("fun2") 1332 .addModifiers(ABSTRACT) 1333 .build(), 1334 ) 1335 .build() 1336 }.hasMessageThat() 1337 .contains("Functional interfaces must have exactly one abstract function. Contained 2") 1338 } 1339 nestedClassesnull1340 @Test fun nestedClasses() { 1341 val taco = ClassName(tacosPackage, "Combo", "Taco") 1342 val topping = ClassName(tacosPackage, "Combo", "Taco", "Topping") 1343 val chips = ClassName(tacosPackage, "Combo", "Chips") 1344 val sauce = ClassName(tacosPackage, "Combo", "Sauce") 1345 val typeSpec = TypeSpec.classBuilder("Combo") 1346 .addProperty("taco", taco) 1347 .addProperty("chips", chips) 1348 .addType( 1349 TypeSpec.classBuilder(taco.simpleName) 1350 .addProperty("toppings", List::class.asClassName().parameterizedBy(topping)) 1351 .addProperty("sauce", sauce) 1352 .addType( 1353 TypeSpec.enumBuilder(topping.simpleName) 1354 .addEnumConstant("SHREDDED_CHEESE") 1355 .addEnumConstant("LEAN_GROUND_BEEF") 1356 .build(), 1357 ) 1358 .build(), 1359 ) 1360 .addType( 1361 TypeSpec.classBuilder(chips.simpleName) 1362 .addProperty("topping", topping) 1363 .addProperty("dippingSauce", sauce) 1364 .build(), 1365 ) 1366 .addType( 1367 TypeSpec.enumBuilder(sauce.simpleName) 1368 .addEnumConstant("SOUR_CREAM") 1369 .addEnumConstant("SALSA") 1370 .addEnumConstant("QUESO") 1371 .addEnumConstant("MILD") 1372 .addEnumConstant("FIRE") 1373 .build(), 1374 ) 1375 .build() 1376 1377 assertThat(toString(typeSpec)).isEqualTo( 1378 """ 1379 |package com.squareup.tacos 1380 | 1381 |import kotlin.collections.List 1382 | 1383 |public class Combo { 1384 | public val taco: Taco 1385 | 1386 | public val chips: Chips 1387 | 1388 | public class Taco { 1389 | public val toppings: List<Topping> 1390 | 1391 | public val sauce: Sauce 1392 | 1393 | public enum class Topping { 1394 | SHREDDED_CHEESE, 1395 | LEAN_GROUND_BEEF, 1396 | } 1397 | } 1398 | 1399 | public class Chips { 1400 | public val topping: Taco.Topping 1401 | 1402 | public val dippingSauce: Sauce 1403 | } 1404 | 1405 | public enum class Sauce { 1406 | SOUR_CREAM, 1407 | SALSA, 1408 | QUESO, 1409 | MILD, 1410 | FIRE, 1411 | } 1412 |} 1413 | 1414 """.trimMargin(), 1415 ) 1416 } 1417 annotationnull1418 @Test fun annotation() { 1419 val annotation = TypeSpec.annotationBuilder("MyAnnotation") 1420 .addModifiers(KModifier.PUBLIC) 1421 .primaryConstructor( 1422 FunSpec.constructorBuilder() 1423 .addParameter( 1424 ParameterSpec.builder("test", Int::class) 1425 .build(), 1426 ) 1427 .build(), 1428 ) 1429 .addProperty( 1430 PropertySpec.builder("test", Int::class) 1431 .initializer("test") 1432 .build(), 1433 ) 1434 .build() 1435 1436 assertThat(toString(annotation)).isEqualTo( 1437 """ 1438 |package com.squareup.tacos 1439 | 1440 |import kotlin.Int 1441 | 1442 |public annotation class MyAnnotation( 1443 | public val test: Int, 1444 |) 1445 | 1446 """.trimMargin(), 1447 ) 1448 } 1449 annotationWithNestedTypesnull1450 @Test fun annotationWithNestedTypes() { 1451 val annotationName = ClassName(tacosPackage, "TacoDelivery") 1452 val kindName = annotationName.nestedClass("Kind") 1453 val annotation = TypeSpec.annotationBuilder(annotationName) 1454 .addModifiers(PUBLIC) 1455 .primaryConstructor( 1456 FunSpec.constructorBuilder() 1457 .addParameter( 1458 ParameterSpec.builder("kind", kindName) 1459 .build(), 1460 ) 1461 .addParameter( 1462 ParameterSpec.builder("quantity", Int::class) 1463 .defaultValue("QUANTITY_DEFAULT") 1464 .build(), 1465 ) 1466 .build(), 1467 ) 1468 .addProperty( 1469 PropertySpec.builder("kind", kindName) 1470 .initializer("kind") 1471 .build(), 1472 ) 1473 .addProperty( 1474 PropertySpec.builder("quantity", Int::class) 1475 .initializer("quantity") 1476 .build(), 1477 ) 1478 .addType( 1479 TypeSpec.enumBuilder("Kind") 1480 .addEnumConstant("SOFT") 1481 .addEnumConstant("HARD") 1482 .build(), 1483 ) 1484 .addType( 1485 TypeSpec.companionObjectBuilder() 1486 .addProperty( 1487 PropertySpec 1488 .builder("QUANTITY_DEFAULT", Int::class, KModifier.CONST) 1489 .initializer("%L", 10_000) 1490 .build(), 1491 ) 1492 .build(), 1493 ) 1494 .build() 1495 1496 assertThat(toString(annotation)).isEqualTo( 1497 """ 1498 |package com.squareup.tacos 1499 | 1500 |import kotlin.Int 1501 | 1502 |public annotation class TacoDelivery( 1503 | public val kind: Kind, 1504 | public val quantity: Int = QUANTITY_DEFAULT, 1505 |) { 1506 | public enum class Kind { 1507 | SOFT, 1508 | HARD, 1509 | } 1510 | 1511 | public companion object { 1512 | public const val QUANTITY_DEFAULT: Int = 10_000 1513 | } 1514 |} 1515 | 1516 """.trimMargin(), 1517 ) 1518 } 1519 1520 @Ignore @Test innerAnnotationInAnnotationDeclarationnull1521 fun innerAnnotationInAnnotationDeclaration() { 1522 val bar = TypeSpec.annotationBuilder("Bar") 1523 .primaryConstructor( 1524 FunSpec.constructorBuilder() 1525 .addParameter( 1526 ParameterSpec.builder("value", java.lang.Deprecated::class) 1527 .build(), 1528 ) 1529 .build(), 1530 ) 1531 .addProperty( 1532 PropertySpec.builder("value", java.lang.Deprecated::class) 1533 .initializer("value") 1534 .build(), 1535 ) 1536 .build() 1537 1538 assertThat(toString(bar)).isEqualTo( 1539 """ 1540 |package com.squareup.tacos 1541 | 1542 |import java.lang.Deprecated 1543 | 1544 |annotation class Bar() { 1545 | fun value(): Deprecated default @Deprecated 1546 |} 1547 | 1548 """.trimMargin(), 1549 ) 1550 } 1551 interfaceWithPropertiesnull1552 @Test fun interfaceWithProperties() { 1553 val taco = TypeSpec.interfaceBuilder("Taco") 1554 .addProperty("v", Int::class) 1555 .build() 1556 1557 assertThat(toString(taco)).isEqualTo( 1558 """ 1559 |package com.squareup.tacos 1560 | 1561 |import kotlin.Int 1562 | 1563 |public interface Taco { 1564 | public val v: Int 1565 |} 1566 | 1567 """.trimMargin(), 1568 ) 1569 } 1570 expectClassnull1571 @Test fun expectClass() { 1572 val classA = TypeSpec.classBuilder("ClassA") 1573 .addModifiers(KModifier.EXPECT) 1574 .addFunction( 1575 FunSpec.builder("test") 1576 .build(), 1577 ) 1578 .build() 1579 1580 assertThat(classA.toString()).isEqualTo( 1581 """ 1582 |public expect class ClassA { 1583 | public fun test() 1584 |} 1585 | 1586 """.trimMargin(), 1587 ) 1588 } 1589 nestedExpectCompanionObjectWithFunctionnull1590 @Test fun nestedExpectCompanionObjectWithFunction() { 1591 val classA = TypeSpec.classBuilder("ClassA") 1592 .addModifiers(KModifier.EXPECT) 1593 .addType( 1594 TypeSpec.companionObjectBuilder() 1595 .addFunction( 1596 FunSpec.builder("test") 1597 .build(), 1598 ) 1599 .build(), 1600 ) 1601 .build() 1602 1603 assertThat(classA.toString()).isEqualTo( 1604 """ 1605 |public expect class ClassA { 1606 | public companion object { 1607 | public fun test() 1608 | } 1609 |} 1610 | 1611 """.trimMargin(), 1612 ) 1613 } 1614 nestedExpectClassWithFunctionnull1615 @Test fun nestedExpectClassWithFunction() { 1616 val classA = TypeSpec.classBuilder("ClassA") 1617 .addModifiers(KModifier.EXPECT) 1618 .addType( 1619 TypeSpec.classBuilder("ClassB") 1620 .addFunction( 1621 FunSpec.builder("test") 1622 .build(), 1623 ) 1624 .build(), 1625 ) 1626 .build() 1627 1628 assertThat(classA.toString()).isEqualTo( 1629 """ 1630 |public expect class ClassA { 1631 | public class ClassB { 1632 | public fun test() 1633 | } 1634 |} 1635 | 1636 """.trimMargin(), 1637 ) 1638 } 1639 deeplyNestedExpectClassWithFunctionnull1640 @Test fun deeplyNestedExpectClassWithFunction() { 1641 val classA = TypeSpec.classBuilder("ClassA") 1642 .addModifiers(KModifier.EXPECT) 1643 .addType( 1644 TypeSpec.classBuilder("ClassB") 1645 .addType( 1646 TypeSpec.classBuilder("ClassC") 1647 .addFunction( 1648 FunSpec.builder("test") 1649 .build(), 1650 ) 1651 .build(), 1652 ) 1653 .build(), 1654 ) 1655 .build() 1656 1657 assertThat(classA.toString()).isEqualTo( 1658 """ 1659 |public expect class ClassA { 1660 | public class ClassB { 1661 | public class ClassC { 1662 | public fun test() 1663 | } 1664 | } 1665 |} 1666 | 1667 """.trimMargin(), 1668 ) 1669 } 1670 veryDeeplyNestedExpectClassWithFunctionnull1671 @Test fun veryDeeplyNestedExpectClassWithFunction() { 1672 val classA = TypeSpec.classBuilder("ClassA") 1673 .addModifiers(KModifier.EXPECT) 1674 .addType( 1675 TypeSpec.classBuilder("ClassB") 1676 .addType( 1677 TypeSpec.classBuilder("ClassC") 1678 .addType( 1679 TypeSpec.classBuilder("ClassD") 1680 .addFunction( 1681 FunSpec.builder("test") 1682 .build(), 1683 ) 1684 .build(), 1685 ) 1686 .build(), 1687 ) 1688 .build(), 1689 ) 1690 .build() 1691 1692 assertThat(classA.toString()).isEqualTo( 1693 """ 1694 |public expect class ClassA { 1695 | public class ClassB { 1696 | public class ClassC { 1697 | public class ClassD { 1698 | public fun test() 1699 | } 1700 | } 1701 | } 1702 |} 1703 | 1704 """.trimMargin(), 1705 ) 1706 } 1707 deeplyNestedExpectClassWithConstructornull1708 @Test fun deeplyNestedExpectClassWithConstructor() { 1709 val classA = TypeSpec.classBuilder("ClassA") 1710 .addModifiers(KModifier.EXPECT) 1711 .addType( 1712 TypeSpec.classBuilder("ClassB") 1713 .addType( 1714 TypeSpec.classBuilder("ClassC") 1715 .addFunction( 1716 FunSpec.constructorBuilder() 1717 .build(), 1718 ) 1719 .build(), 1720 ) 1721 .build(), 1722 ) 1723 .build() 1724 1725 assertThat(classA.toString()).isEqualTo( 1726 """ 1727 |public expect class ClassA { 1728 | public class ClassB { 1729 | public class ClassC { 1730 | public constructor() 1731 | } 1732 | } 1733 |} 1734 | 1735 """.trimMargin(), 1736 ) 1737 } 1738 veryDeeplyNestedExpectClassWithConstructornull1739 @Test fun veryDeeplyNestedExpectClassWithConstructor() { 1740 val classA = TypeSpec.classBuilder("ClassA") 1741 .addModifiers(KModifier.EXPECT) 1742 .addType( 1743 TypeSpec.classBuilder("ClassB") 1744 .addType( 1745 TypeSpec.classBuilder("ClassC") 1746 .addType( 1747 TypeSpec.classBuilder("ClassD") 1748 .addFunction( 1749 FunSpec.constructorBuilder() 1750 .build(), 1751 ) 1752 .build(), 1753 ) 1754 .build(), 1755 ) 1756 .build(), 1757 ) 1758 .build() 1759 1760 assertThat(classA.toString()).isEqualTo( 1761 """ 1762 |public expect class ClassA { 1763 | public class ClassB { 1764 | public class ClassC { 1765 | public class ClassD { 1766 | public constructor() 1767 | } 1768 | } 1769 | } 1770 |} 1771 | 1772 """.trimMargin(), 1773 ) 1774 } 1775 interfaceWithMethodsnull1776 @Test fun interfaceWithMethods() { 1777 val taco = TypeSpec.interfaceBuilder("Taco") 1778 .addFunction( 1779 FunSpec.builder("aMethod") 1780 .addModifiers(KModifier.ABSTRACT) 1781 .build(), 1782 ) 1783 .addFunction(FunSpec.builder("aDefaultMethod").build()) 1784 .addFunction( 1785 FunSpec.builder("aPrivateMethod") 1786 .addModifiers(KModifier.PRIVATE) 1787 .build(), 1788 ) 1789 .build() 1790 1791 assertThat(toString(taco)).isEqualTo( 1792 """ 1793 |package com.squareup.tacos 1794 | 1795 |public interface Taco { 1796 | public fun aMethod() 1797 | 1798 | public fun aDefaultMethod() { 1799 | } 1800 | 1801 | private fun aPrivateMethod() { 1802 | } 1803 |} 1804 | 1805 """.trimMargin(), 1806 ) 1807 } 1808 referencedAndDeclaredSimpleNamesConflictnull1809 @Test fun referencedAndDeclaredSimpleNamesConflict() { 1810 val internalTop = PropertySpec.builder( 1811 "internalTop", 1812 ClassName(tacosPackage, "Top"), 1813 ).build() 1814 val internalBottom = PropertySpec.builder( 1815 "internalBottom", 1816 ClassName(tacosPackage, "Top", "Middle", "Bottom"), 1817 ).build() 1818 val externalTop = PropertySpec.builder( 1819 "externalTop", 1820 ClassName(donutsPackage, "Top"), 1821 ).build() 1822 val externalBottom = PropertySpec.builder( 1823 "externalBottom", 1824 ClassName(donutsPackage, "Bottom"), 1825 ).build() 1826 val top = TypeSpec.classBuilder("Top") 1827 .addProperty(internalTop) 1828 .addProperty(internalBottom) 1829 .addProperty(externalTop) 1830 .addProperty(externalBottom) 1831 .addType( 1832 TypeSpec.classBuilder("Middle") 1833 .addProperty(internalTop) 1834 .addProperty(internalBottom) 1835 .addProperty(externalTop) 1836 .addProperty(externalBottom) 1837 .addType( 1838 TypeSpec.classBuilder("Bottom") 1839 .addProperty(internalTop) 1840 .addProperty(internalBottom) 1841 .addProperty(externalTop) 1842 .addProperty(externalBottom) 1843 .build(), 1844 ) 1845 .build(), 1846 ) 1847 .build() 1848 assertThat(toString(top)).isEqualTo( 1849 """ 1850 |package com.squareup.tacos 1851 | 1852 |import com.squareup.donuts.Bottom 1853 | 1854 |public class Top { 1855 | public val internalTop: Top 1856 | 1857 | public val internalBottom: Middle.Bottom 1858 | 1859 | public val externalTop: com.squareup.donuts.Top 1860 | 1861 | public val externalBottom: Bottom 1862 | 1863 | public class Middle { 1864 | public val internalTop: Top 1865 | 1866 | public val internalBottom: Bottom 1867 | 1868 | public val externalTop: com.squareup.donuts.Top 1869 | 1870 | public val externalBottom: com.squareup.donuts.Bottom 1871 | 1872 | public class Bottom { 1873 | public val internalTop: Top 1874 | 1875 | public val internalBottom: Bottom 1876 | 1877 | public val externalTop: com.squareup.donuts.Top 1878 | 1879 | public val externalBottom: com.squareup.donuts.Bottom 1880 | } 1881 | } 1882 |} 1883 | 1884 """.trimMargin(), 1885 ) 1886 } 1887 simpleNamesConflictInThisAndOtherPackagenull1888 @Test fun simpleNamesConflictInThisAndOtherPackage() { 1889 val internalOther = PropertySpec.builder( 1890 "internalOther", 1891 ClassName(tacosPackage, "Other"), 1892 ).build() 1893 val externalOther = PropertySpec.builder( 1894 "externalOther", 1895 ClassName(donutsPackage, "Other"), 1896 ).build() 1897 val gen = TypeSpec.classBuilder("Gen") 1898 .addProperty(internalOther) 1899 .addProperty(externalOther) 1900 .build() 1901 assertThat(toString(gen)).isEqualTo( 1902 """ 1903 |package com.squareup.tacos 1904 | 1905 |public class Gen { 1906 | public val internalOther: Other 1907 | 1908 | public val externalOther: com.squareup.donuts.Other 1909 |} 1910 | 1911 """.trimMargin(), 1912 ) 1913 } 1914 intersectionTypenull1915 @Test fun intersectionType() { 1916 val typeVariable = TypeVariableName("T", Comparator::class, Serializable::class) 1917 val taco = TypeSpec.classBuilder("Taco") 1918 .addFunction( 1919 FunSpec.builder("getComparator") 1920 .addTypeVariable(typeVariable) 1921 .returns(typeVariable) 1922 .addStatement("return null") 1923 .build(), 1924 ) 1925 .build() 1926 assertThat(toString(taco)).isEqualTo( 1927 """ 1928 |package com.squareup.tacos 1929 | 1930 |import java.io.Serializable 1931 |import java.util.Comparator 1932 | 1933 |public class Taco { 1934 | public fun <T> getComparator(): T where T : Comparator, T : Serializable = null 1935 |} 1936 | 1937 """.trimMargin(), 1938 ) 1939 } 1940 primitiveArrayTypenull1941 @Test fun primitiveArrayType() { 1942 val taco = TypeSpec.classBuilder("Taco") 1943 .addProperty("ints", IntArray::class) 1944 .build() 1945 assertThat(toString(taco)).isEqualTo( 1946 """ 1947 |package com.squareup.tacos 1948 | 1949 |import kotlin.IntArray 1950 | 1951 |public class Taco { 1952 | public val ints: IntArray 1953 |} 1954 | 1955 """.trimMargin(), 1956 ) 1957 } 1958 kdocnull1959 @Test fun kdoc() { 1960 val taco = TypeSpec.classBuilder("Taco") 1961 .addKdoc("A hard or soft tortilla, loosely folded and filled with whatever\n") 1962 .addKdoc("[random][%T] tex-mex stuff we could find in the pantry\n", Random::class) 1963 .addKdoc(CodeBlock.of("and some [%T] cheese.\n", String::class)) 1964 .addProperty( 1965 PropertySpec.builder("soft", Boolean::class) 1966 .addKdoc("True for a soft flour tortilla; false for a crunchy corn tortilla.\n") 1967 .build(), 1968 ) 1969 .addFunction( 1970 FunSpec.builder("refold") 1971 .addKdoc( 1972 "Folds the back of this taco to reduce sauce leakage.\n" + 1973 "\n" + 1974 "For [%T#KOREAN], the front may also be folded.\n", 1975 Locale::class, 1976 ) 1977 .addParameter("locale", Locale::class) 1978 .build(), 1979 ) 1980 .build() 1981 // Mentioning a type in KDoc will not cause an import to be added (java.util.Random here), but 1982 // the short name will be used if it's already imported (java.util.Locale here). 1983 assertThat(toString(taco)).isEqualTo( 1984 """ 1985 |package com.squareup.tacos 1986 | 1987 |import java.util.Locale 1988 |import kotlin.Boolean 1989 | 1990 |/** 1991 | * A hard or soft tortilla, loosely folded and filled with whatever 1992 | * [random][java.util.Random] tex-mex stuff we could find in the pantry 1993 | * and some [kotlin.String] cheese. 1994 | */ 1995 |public class Taco { 1996 | /** 1997 | * True for a soft flour tortilla; false for a crunchy corn tortilla. 1998 | */ 1999 | public val soft: Boolean 2000 | 2001 | /** 2002 | * Folds the back of this taco to reduce sauce leakage. 2003 | * 2004 | * For [Locale#KOREAN], the front may also be folded. 2005 | */ 2006 | public fun refold(locale: Locale) { 2007 | } 2008 |} 2009 | 2010 """.trimMargin(), 2011 ) 2012 } 2013 kdocWithParametersnull2014 @Test fun kdocWithParameters() { 2015 val taco = TypeSpec.classBuilder("Taco") 2016 .addKdoc("A hard or soft tortilla, loosely folded and filled with whatever\n") 2017 .addKdoc("[random][%T] tex-mex stuff we could find in the pantry\n", Random::class) 2018 .addKdoc(CodeBlock.of("and some [%T] cheese.\n", String::class)) 2019 .primaryConstructor( 2020 FunSpec.constructorBuilder() 2021 .addParameter( 2022 ParameterSpec.builder("temperature", Double::class) 2023 .addKdoc( 2024 CodeBlock.of( 2025 "%L", 2026 """ 2027 |Taco temperature. Can be as cold as the famous ice tacos from 2028 |the Andes, or hot with lava-like cheese from the depths of 2029 |the Ninth Circle. 2030 | 2031 """.trimMargin(), 2032 ), 2033 ) 2034 .build(), 2035 ) 2036 .addParameter("soft", Boolean::class) 2037 .addParameter( 2038 ParameterSpec.builder("mild", Boolean::class) 2039 .addKdoc(CodeBlock.of("%L", "Whether the taco is mild (ew) or crunchy (ye).\n")) 2040 .build(), 2041 ) 2042 .addParameter("nodoc", Int::class) 2043 .build(), 2044 ) 2045 .addProperty( 2046 PropertySpec.builder("soft", Boolean::class) 2047 .addKdoc("True for a soft flour tortilla; false for a crunchy corn tortilla.\n") 2048 .initializer("soft") 2049 .build(), 2050 ) 2051 .addProperty( 2052 PropertySpec.builder("mild", Boolean::class) 2053 .addKdoc("No one likes mild tacos.") 2054 .initializer("mild") 2055 .build(), 2056 ) 2057 .addProperty( 2058 PropertySpec.builder("nodoc", Int::class, KModifier.PRIVATE) 2059 .initializer("nodoc") 2060 .build(), 2061 ) 2062 .build() 2063 assertThat(toString(taco)).isEqualTo( 2064 """ 2065 |package com.squareup.tacos 2066 | 2067 |import kotlin.Boolean 2068 |import kotlin.Double 2069 |import kotlin.Int 2070 | 2071 |/** 2072 | * A hard or soft tortilla, loosely folded and filled with whatever 2073 | * [random][java.util.Random] tex-mex stuff we could find in the pantry 2074 | * and some [kotlin.String] cheese. 2075 | * 2076 | * @param temperature Taco temperature. Can be as cold as the famous ice tacos from 2077 | * the Andes, or hot with lava-like cheese from the depths of 2078 | * the Ninth Circle. 2079 | * @param mild Whether the taco is mild (ew) or crunchy (ye). 2080 | */ 2081 |public class Taco( 2082 | temperature: Double, 2083 | /** 2084 | * True for a soft flour tortilla; false for a crunchy corn tortilla. 2085 | */ 2086 | public val soft: Boolean, 2087 | /** 2088 | * No one likes mild tacos. 2089 | */ 2090 | public val mild: Boolean, 2091 | private val nodoc: Int, 2092 |) 2093 | 2094 """.trimMargin(), 2095 ) 2096 } 2097 annotationsInAnnotationsnull2098 @Test fun annotationsInAnnotations() { 2099 val beef = ClassName(tacosPackage, "Beef") 2100 val chicken = ClassName(tacosPackage, "Chicken") 2101 val option = ClassName(tacosPackage, "Option") 2102 val mealDeal = ClassName(tacosPackage, "MealDeal") 2103 val menu = TypeSpec.classBuilder("Menu") 2104 .addAnnotation( 2105 AnnotationSpec.builder(mealDeal) 2106 .addMember("%L = %L", "price", 500) 2107 .addMember( 2108 "%L = [%L, %L]", 2109 "options", 2110 AnnotationSpec.builder(option) 2111 .addMember("%S", "taco") 2112 .addMember("%T::class", beef) 2113 .build(), 2114 AnnotationSpec.builder(option) 2115 .addMember("%S", "quesadilla") 2116 .addMember("%T::class", chicken) 2117 .build(), 2118 ) 2119 .build(), 2120 ) 2121 .build() 2122 assertThat(toString(menu)).isEqualTo( 2123 """ 2124 |package com.squareup.tacos 2125 | 2126 |@MealDeal( 2127 | price = 500, 2128 | options = [Option("taco", Beef::class), Option("quesadilla", Chicken::class)], 2129 |) 2130 |public class Menu 2131 | 2132 """.trimMargin(), 2133 ) 2134 } 2135 varargsnull2136 @Test fun varargs() { 2137 val taqueria = TypeSpec.classBuilder("Taqueria") 2138 .addFunction( 2139 FunSpec.builder("prepare") 2140 .addParameter("workers", Int::class) 2141 .addParameter("jobs", Runnable::class.asClassName(), VARARG) 2142 .build(), 2143 ) 2144 .build() 2145 assertThat(toString(taqueria)).isEqualTo( 2146 """ 2147 |package com.squareup.tacos 2148 | 2149 |import java.lang.Runnable 2150 |import kotlin.Int 2151 | 2152 |public class Taqueria { 2153 | public fun prepare(workers: Int, vararg jobs: Runnable) { 2154 | } 2155 |} 2156 | 2157 """.trimMargin(), 2158 ) 2159 } 2160 varargsNotLastnull2161 @Test fun varargsNotLast() { 2162 val taqueria = TypeSpec.classBuilder("Taqueria") 2163 .addFunction( 2164 FunSpec.builder("prepare") 2165 .addParameter("workers", Int::class) 2166 .addParameter("jobs", Runnable::class.asClassName(), VARARG) 2167 .addParameter("start", Boolean::class.asClassName()) 2168 .build(), 2169 ) 2170 .build() 2171 assertThat(toString(taqueria)).isEqualTo( 2172 """ 2173 |package com.squareup.tacos 2174 | 2175 |import java.lang.Runnable 2176 |import kotlin.Boolean 2177 |import kotlin.Int 2178 | 2179 |public class Taqueria { 2180 | public fun prepare( 2181 | workers: Int, 2182 | vararg jobs: Runnable, 2183 | start: Boolean, 2184 | ) { 2185 | } 2186 |} 2187 | 2188 """.trimMargin(), 2189 ) 2190 } 2191 codeBlocksnull2192 @Test fun codeBlocks() { 2193 val ifBlock = CodeBlock.builder() 2194 .beginControlFlow("if (a != b)") 2195 .addStatement("return i") 2196 .endControlFlow() 2197 .build() 2198 val funBody = CodeBlock.builder() 2199 .addStatement("val size = %T.min(listA.size, listB.size)", Math::class) 2200 .beginControlFlow("for (i in 0..<size)") 2201 .addStatement("val %N = %N[i]", "a", "listA") 2202 .addStatement("val %N = %N[i]", "b", "listB") 2203 .add("%L", ifBlock) 2204 .endControlFlow() 2205 .addStatement("return size") 2206 .build() 2207 val propertyBlock = CodeBlock.builder() 2208 .add("%T.<%T, %T>builder()", ImmutableMap::class, String::class, String::class) 2209 .add("\n.add(%S, %S)", '\'', "'") 2210 .add("\n.add(%S, %S)", '&', "&") 2211 .add("\n.add(%S, %S)", '<', "<") 2212 .add("\n.add(%S, %S)", '>', ">") 2213 .add("\n.build()") 2214 .build() 2215 val escapeHtml = PropertySpec.builder( 2216 "ESCAPE_HTML", 2217 Map::class.parameterizedBy(String::class, String::class), 2218 ) 2219 .addModifiers(KModifier.PRIVATE) 2220 .initializer(propertyBlock) 2221 .build() 2222 val util = TypeSpec.classBuilder("Util") 2223 .addProperty(escapeHtml) 2224 .addFunction( 2225 FunSpec.builder("commonPrefixLength") 2226 .returns(Int::class) 2227 .addParameter("listA", List::class.parameterizedBy(String::class)) 2228 .addParameter("listB", List::class.parameterizedBy(String::class)) 2229 .addCode(funBody) 2230 .build(), 2231 ) 2232 .build() 2233 assertThat(toString(util)).isEqualTo( 2234 """ 2235 |package com.squareup.tacos 2236 | 2237 |import com.google.common.collect.ImmutableMap 2238 |import java.lang.Math 2239 |import kotlin.Int 2240 |import kotlin.String 2241 |import kotlin.collections.List 2242 |import kotlin.collections.Map 2243 | 2244 |public class Util { 2245 | private val ESCAPE_HTML: Map<String, String> = ImmutableMap.<String, String>builder() 2246 | .add("'", "'") 2247 | .add("&", "&") 2248 | .add("<", "<") 2249 | .add(">", ">") 2250 | .build() 2251 | 2252 | public fun commonPrefixLength(listA: List<String>, listB: List<String>): Int { 2253 | val size = Math.min(listA.size, listB.size) 2254 | for (i in 0..<size) { 2255 | val a = listA[i] 2256 | val b = listB[i] 2257 | if (a != b) { 2258 | return i 2259 | } 2260 | } 2261 | return size 2262 | } 2263 |} 2264 | 2265 """.trimMargin(), 2266 ) 2267 } 2268 indexedElseIfnull2269 @Test fun indexedElseIf() { 2270 val taco = TypeSpec.classBuilder("Taco") 2271 .addFunction( 2272 FunSpec.builder("choices") 2273 .beginControlFlow("if (%1L != null || %1L == %2L)", "taco", "otherTaco") 2274 .addStatement("%T.out.println(%S)", System::class, "only one taco? NOO!") 2275 .nextControlFlow("else if (%1L.%3L && %2L.%3L)", "taco", "otherTaco", "isSupreme()") 2276 .addStatement("%T.out.println(%S)", System::class, "taco heaven") 2277 .endControlFlow() 2278 .build(), 2279 ) 2280 .build() 2281 assertThat(toString(taco)).isEqualTo( 2282 """ 2283 |package com.squareup.tacos 2284 | 2285 |import java.lang.System 2286 | 2287 |public class Taco { 2288 | public fun choices() { 2289 | if (taco != null || taco == otherTaco) { 2290 | System.out.println("only one taco? NOO!") 2291 | } else if (taco.isSupreme() && otherTaco.isSupreme()) { 2292 | System.out.println("taco heaven") 2293 | } 2294 | } 2295 |} 2296 | 2297 """.trimMargin(), 2298 ) 2299 } 2300 elseIfnull2301 @Test fun elseIf() { 2302 val taco = TypeSpec.classBuilder("Taco") 2303 .addFunction( 2304 FunSpec.builder("choices") 2305 .beginControlFlow("if (5 < 4) ") 2306 .addStatement("%T.out.println(%S)", System::class, "wat") 2307 .nextControlFlow("else if (5 < 6)") 2308 .addStatement("%T.out.println(%S)", System::class, "hello") 2309 .endControlFlow() 2310 .build(), 2311 ) 2312 .build() 2313 assertThat(toString(taco)).isEqualTo( 2314 """ 2315 |package com.squareup.tacos 2316 | 2317 |import java.lang.System 2318 | 2319 |public class Taco { 2320 | public fun choices() { 2321 | if (5 < 4) { 2322 | System.out.println("wat") 2323 | } else if (5 < 6) { 2324 | System.out.println("hello") 2325 | } 2326 | } 2327 |} 2328 | 2329 """.trimMargin(), 2330 ) 2331 } 2332 inlineIndentnull2333 @Test fun inlineIndent() { 2334 val taco = TypeSpec.classBuilder("Taco") 2335 .addFunction( 2336 FunSpec.builder("inlineIndent") 2337 .addCode("if (3 < 4) {\n⇥%T.out.println(%S);\n⇤}\n", System::class, "hello") 2338 .build(), 2339 ) 2340 .build() 2341 assertThat(toString(taco)).isEqualTo( 2342 """ 2343 |package com.squareup.tacos 2344 | 2345 |import java.lang.System 2346 | 2347 |public class Taco { 2348 | public fun inlineIndent() { 2349 | if (3 < 4) { 2350 | System.out.println("hello"); 2351 | } 2352 | } 2353 |} 2354 | 2355 """.trimMargin(), 2356 ) 2357 } 2358 defaultModifiersForMemberInterfacesAndEnumsnull2359 @Test fun defaultModifiersForMemberInterfacesAndEnums() { 2360 val taco = TypeSpec.classBuilder("Taco") 2361 .addType( 2362 TypeSpec.classBuilder("Meat") 2363 .build(), 2364 ) 2365 .addType( 2366 TypeSpec.interfaceBuilder("Tortilla") 2367 .build(), 2368 ) 2369 .addType( 2370 TypeSpec.enumBuilder("Topping") 2371 .addEnumConstant("SALSA") 2372 .build(), 2373 ) 2374 .build() 2375 assertThat(toString(taco)).isEqualTo( 2376 """ 2377 |package com.squareup.tacos 2378 | 2379 |public class Taco { 2380 | public class Meat 2381 | 2382 | public interface Tortilla 2383 | 2384 | public enum class Topping { 2385 | SALSA, 2386 | } 2387 |} 2388 | 2389 """.trimMargin(), 2390 ) 2391 } 2392 membersOrderingnull2393 @Test fun membersOrdering() { 2394 // Hand out names in reverse-alphabetical order to defend against unexpected sorting. 2395 val taco = TypeSpec.classBuilder("Members") 2396 .addType(TypeSpec.classBuilder("Z").build()) 2397 .addType(TypeSpec.classBuilder("Y").build()) 2398 .addProperty("W", String::class) 2399 .addProperty("U", String::class) 2400 .addFunction(FunSpec.builder("T").build()) 2401 .addFunction(FunSpec.builder("S").build()) 2402 .addFunction(FunSpec.builder("R").build()) 2403 .addFunction(FunSpec.builder("Q").build()) 2404 .addFunction( 2405 FunSpec.constructorBuilder() 2406 .addParameter("p", Int::class) 2407 .build(), 2408 ) 2409 .addFunction( 2410 FunSpec.constructorBuilder() 2411 .addParameter("o", Long::class) 2412 .build(), 2413 ) 2414 .build() 2415 // Static properties, instance properties, constructors, functions, classes. 2416 assertThat(toString(taco)).isEqualTo( 2417 """ 2418 |package com.squareup.tacos 2419 | 2420 |import kotlin.Int 2421 |import kotlin.Long 2422 |import kotlin.String 2423 | 2424 |public class Members { 2425 | public val W: String 2426 | 2427 | public val U: String 2428 | 2429 | public constructor(p: Int) 2430 | 2431 | public constructor(o: Long) 2432 | 2433 | public fun T() { 2434 | } 2435 | 2436 | public fun S() { 2437 | } 2438 | 2439 | public fun R() { 2440 | } 2441 | 2442 | public fun Q() { 2443 | } 2444 | 2445 | public class Z 2446 | 2447 | public class Y 2448 |} 2449 | 2450 """.trimMargin(), 2451 ) 2452 } 2453 nativeFunctionsnull2454 @Test fun nativeFunctions() { 2455 val taco = TypeSpec.classBuilder("Taco") 2456 .addFunction( 2457 FunSpec.builder("nativeInt") 2458 .addModifiers(KModifier.EXTERNAL) 2459 .returns(Int::class) 2460 .build(), 2461 ) 2462 .build() 2463 assertThat(toString(taco)).isEqualTo( 2464 """ 2465 |package com.squareup.tacos 2466 | 2467 |import kotlin.Int 2468 | 2469 |public class Taco { 2470 | public external fun nativeInt(): Int 2471 |} 2472 | 2473 """.trimMargin(), 2474 ) 2475 } 2476 nullStringLiteralnull2477 @Test fun nullStringLiteral() { 2478 val taco = TypeSpec.classBuilder("Taco") 2479 .addProperty( 2480 PropertySpec.builder("NULL", String::class) 2481 .initializer("%S", null) 2482 .build(), 2483 ) 2484 .build() 2485 assertThat(toString(taco)).isEqualTo( 2486 """ 2487 |package com.squareup.tacos 2488 | 2489 |import kotlin.String 2490 | 2491 |public class Taco { 2492 | public val NULL: String = null 2493 |} 2494 | 2495 """.trimMargin(), 2496 ) 2497 } 2498 annotationToStringnull2499 @Test fun annotationToString() { 2500 val annotation = AnnotationSpec.builder(SuppressWarnings::class) 2501 .addMember("%S", "unused") 2502 .build() 2503 assertThat(annotation.toString()).isEqualTo("@java.lang.SuppressWarnings(\"unused\")") 2504 } 2505 codeBlockToStringnull2506 @Test fun codeBlockToString() { 2507 val codeBlock = CodeBlock.builder() 2508 .addStatement("%T %N = %S.substring(0, 3)", String::class, "s", "taco") 2509 .build() 2510 assertThat(codeBlock.toString()).isEqualTo("kotlin.String s = \"taco\".substring(0, 3)\n") 2511 } 2512 propertyToStringnull2513 @Test fun propertyToString() { 2514 val property = PropertySpec.builder("s", String::class) 2515 .initializer("%S.substring(0, 3)", "taco") 2516 .build() 2517 assertThat(property.toString()) 2518 .isEqualTo("val s: kotlin.String = \"taco\".substring(0, 3)\n") 2519 } 2520 functionToStringnull2521 @Test fun functionToString() { 2522 val funSpec = FunSpec.builder("toString") 2523 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 2524 .returns(String::class) 2525 .addStatement("return %S", "taco") 2526 .build() 2527 assertThat(funSpec.toString()) 2528 .isEqualTo("public override fun toString(): kotlin.String = \"taco\"\n") 2529 } 2530 constructorToStringnull2531 @Test fun constructorToString() { 2532 val constructor = FunSpec.constructorBuilder() 2533 .addModifiers(KModifier.PUBLIC) 2534 .addParameter("taco", ClassName(tacosPackage, "Taco")) 2535 .addStatement("this.%N = %N", "taco", "taco") 2536 .build() 2537 assertThat(constructor.toString()).isEqualTo( 2538 "" + 2539 "public constructor(taco: com.squareup.tacos.Taco) {\n" + 2540 " this.taco = taco\n" + 2541 "}\n", 2542 ) 2543 } 2544 parameterToStringnull2545 @Test fun parameterToString() { 2546 val parameter = ParameterSpec.builder("taco", ClassName(tacosPackage, "Taco")) 2547 .addModifiers(KModifier.CROSSINLINE) 2548 .addAnnotation(ClassName("javax.annotation", "Nullable")) 2549 .build() 2550 assertThat(parameter.toString()) 2551 .isEqualTo("@javax.`annotation`.Nullable crossinline taco: com.squareup.tacos.Taco") 2552 } 2553 classToStringnull2554 @Test fun classToString() { 2555 val type = TypeSpec.classBuilder("Taco") 2556 .build() 2557 assertThat(type.toString()).isEqualTo( 2558 "" + 2559 "public class Taco\n", 2560 ) 2561 } 2562 anonymousClassToStringnull2563 @Test fun anonymousClassToString() { 2564 val type = TypeSpec.anonymousClassBuilder() 2565 .addSuperinterface(Runnable::class) 2566 .addFunction( 2567 FunSpec.builder("run") 2568 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 2569 .build(), 2570 ) 2571 .build() 2572 assertThat(type.toString()).isEqualTo( 2573 """ 2574 |object : java.lang.Runnable { 2575 | public override fun run() { 2576 | } 2577 |} 2578 """.trimMargin(), 2579 ) 2580 } 2581 interfaceClassToStringnull2582 @Test fun interfaceClassToString() { 2583 val type = TypeSpec.interfaceBuilder("Taco") 2584 .build() 2585 assertThat(type.toString()).isEqualTo( 2586 """ 2587 |public interface Taco 2588 | 2589 """.trimMargin(), 2590 ) 2591 } 2592 annotationDeclarationToStringnull2593 @Test fun annotationDeclarationToString() { 2594 val type = TypeSpec.annotationBuilder("Taco") 2595 .build() 2596 assertThat(type.toString()).isEqualTo( 2597 """ 2598 |public annotation class Taco 2599 | 2600 """.trimMargin(), 2601 ) 2602 } 2603 toStringnull2604 private fun toString(typeSpec: TypeSpec): String { 2605 return FileSpec.get(tacosPackage, typeSpec).toString() 2606 } 2607 multilineStatementnull2608 @Test fun multilineStatement() { 2609 val taco = TypeSpec.classBuilder("Taco") 2610 .addFunction( 2611 FunSpec.builder("toString") 2612 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 2613 .returns(String::class) 2614 .addStatement( 2615 "val result = %S\n+ %S\n+ %S\n+ %S\n+ %S", 2616 "Taco(", 2617 "beef,", 2618 "lettuce,", 2619 "cheese", 2620 ")", 2621 ) 2622 .addStatement("return result") 2623 .build(), 2624 ) 2625 .build() 2626 assertThat(toString(taco)).isEqualTo( 2627 """ 2628 |package com.squareup.tacos 2629 | 2630 |import kotlin.String 2631 | 2632 |public class Taco { 2633 | public override fun toString(): String { 2634 | val result = "Taco(" 2635 | + "beef," 2636 | + "lettuce," 2637 | + "cheese" 2638 | + ")" 2639 | return result 2640 | } 2641 |} 2642 | 2643 """.trimMargin(), 2644 ) 2645 } 2646 multilineStatementWithAnonymousClassnull2647 @Test fun multilineStatementWithAnonymousClass() { 2648 val stringComparator = Comparator::class.parameterizedBy(String::class) 2649 val listOfString = List::class.parameterizedBy(String::class) 2650 val prefixComparator = TypeSpec.anonymousClassBuilder() 2651 .addSuperinterface(stringComparator) 2652 .addFunction( 2653 FunSpec.builder("compare") 2654 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 2655 .returns(Int::class) 2656 .addParameter("a", String::class) 2657 .addParameter("b", String::class) 2658 .addComment("Prefix the strings and compare them") 2659 .addStatement("return a.substring(0, length)\n" + ".compareTo(b.substring(0, length))") 2660 .build(), 2661 ) 2662 .build() 2663 val taco = TypeSpec.classBuilder("Taco") 2664 .addFunction( 2665 FunSpec.builder("comparePrefix") 2666 .returns(stringComparator) 2667 .addParameter("length", Int::class) 2668 .addComment("Return a new comparator for the target length.") 2669 .addStatement("return %L", prefixComparator) 2670 .build(), 2671 ) 2672 .addFunction( 2673 FunSpec.builder("sortPrefix") 2674 .addParameter("list", listOfString) 2675 .addParameter("length", Int::class) 2676 .addStatement("%T.sort(\nlist,\n%L)", Collections::class, prefixComparator) 2677 .build(), 2678 ) 2679 .build() 2680 assertThat(toString(taco)).isEqualTo( 2681 """ 2682 |package com.squareup.tacos 2683 | 2684 |import java.util.Collections 2685 |import java.util.Comparator 2686 |import kotlin.Int 2687 |import kotlin.String 2688 |import kotlin.collections.List 2689 | 2690 |public class Taco { 2691 | public fun comparePrefix(length: Int): Comparator<String> { 2692 | // Return a new comparator for the target length. 2693 | return object : Comparator<String> { 2694 | public override fun compare(a: String, b: String): Int { 2695 | // Prefix the strings and compare them 2696 | return a.substring(0, length) 2697 | .compareTo(b.substring(0, length)) 2698 | } 2699 | } 2700 | } 2701 | 2702 | public fun sortPrefix(list: List<String>, length: Int) { 2703 | Collections.sort( 2704 | list, 2705 | object : Comparator<String> { 2706 | public override fun compare(a: String, b: String): Int { 2707 | // Prefix the strings and compare them 2708 | return a.substring(0, length) 2709 | .compareTo(b.substring(0, length)) 2710 | } 2711 | }) 2712 | } 2713 |} 2714 | 2715 """.trimMargin(), 2716 ) 2717 } 2718 multilineStringsnull2719 @Test fun multilineStrings() { 2720 val taco = TypeSpec.classBuilder("Taco") 2721 .addProperty( 2722 PropertySpec.builder("toppings", String::class) 2723 .initializer("%S", "shell\nbeef\nlettuce\ncheese\n") 2724 .build(), 2725 ) 2726 .build() 2727 assertThat(toString(taco)).isEqualTo( 2728 """ 2729 |package com.squareup.tacos 2730 | 2731 |import kotlin.String 2732 | 2733 |public class Taco { 2734 | public val toppings: String = ${"\"\"\""} 2735 | |shell 2736 | |beef 2737 | |lettuce 2738 | |cheese 2739 | ${"\"\"\""}.trimMargin() 2740 |} 2741 | 2742 """.trimMargin(), 2743 ) 2744 } 2745 multipleAnnotationAdditionnull2746 @Test fun multipleAnnotationAddition() { 2747 val taco = TypeSpec.classBuilder("Taco") 2748 .addAnnotations( 2749 listOf( 2750 AnnotationSpec.builder(SuppressWarnings::class) 2751 .addMember("%S", "unchecked") 2752 .build(), 2753 AnnotationSpec.builder(Deprecated::class).build(), 2754 ), 2755 ) 2756 .build() 2757 assertThat(toString(taco)).isEqualTo( 2758 """ 2759 |package com.squareup.tacos 2760 | 2761 |import java.lang.Deprecated 2762 |import java.lang.SuppressWarnings 2763 | 2764 |@SuppressWarnings("unchecked") 2765 |@Deprecated 2766 |public class Taco 2767 | 2768 """.trimMargin(), 2769 ) 2770 } 2771 multiplePropertyAdditionnull2772 @Test fun multiplePropertyAddition() { 2773 val taco = TypeSpec.classBuilder("Taco") 2774 .addProperties( 2775 listOf( 2776 PropertySpec.builder("ANSWER", Int::class, KModifier.CONST).build(), 2777 PropertySpec.builder("price", BigDecimal::class, PRIVATE).build(), 2778 ), 2779 ) 2780 .build() 2781 assertThat(toString(taco)).isEqualTo( 2782 """ 2783 |package com.squareup.tacos 2784 | 2785 |import java.math.BigDecimal 2786 |import kotlin.Int 2787 | 2788 |public class Taco { 2789 | public const val ANSWER: Int 2790 | 2791 | private val price: BigDecimal 2792 |} 2793 | 2794 """.trimMargin(), 2795 ) 2796 } 2797 multipleFunctionAdditionnull2798 @Test fun multipleFunctionAddition() { 2799 val taco = TypeSpec.classBuilder("Taco") 2800 .addFunctions( 2801 listOf( 2802 FunSpec.builder("getAnswer") 2803 .addModifiers(PUBLIC) 2804 .returns(Int::class) 2805 .addStatement("return %L", 42) 2806 .build(), 2807 FunSpec.builder("getRandomQuantity") 2808 .addModifiers(PUBLIC) 2809 .returns(Int::class) 2810 .addKdoc("chosen by fair dice roll ;)\n") 2811 .addStatement("return %L", 4) 2812 .build(), 2813 ), 2814 ) 2815 .build() 2816 assertThat(toString(taco)).isEqualTo( 2817 """ 2818 |package com.squareup.tacos 2819 | 2820 |import kotlin.Int 2821 | 2822 |public class Taco { 2823 | public fun getAnswer(): Int = 42 2824 | 2825 | /** 2826 | * chosen by fair dice roll ;) 2827 | */ 2828 | public fun getRandomQuantity(): Int = 4 2829 |} 2830 | 2831 """.trimMargin(), 2832 ) 2833 } 2834 multipleSuperinterfaceAdditionnull2835 @Test fun multipleSuperinterfaceAddition() { 2836 val taco = TypeSpec.classBuilder("Taco") 2837 .addSuperinterfaces( 2838 listOf( 2839 Serializable::class.asTypeName(), 2840 EventListener::class.asTypeName(), 2841 ), 2842 ) 2843 .build() 2844 assertThat(toString(taco)).isEqualTo( 2845 """ 2846 |package com.squareup.tacos 2847 | 2848 |import java.io.Serializable 2849 |import java.util.EventListener 2850 | 2851 |public class Taco : Serializable, EventListener 2852 | 2853 """.trimMargin(), 2854 ) 2855 } 2856 multipleTypeVariableAdditionnull2857 @Test fun multipleTypeVariableAddition() { 2858 val location = TypeSpec.classBuilder("Location") 2859 .addTypeVariables( 2860 listOf( 2861 TypeVariableName("T"), 2862 TypeVariableName("P", Number::class), 2863 ), 2864 ) 2865 .build() 2866 assertThat(toString(location)).isEqualTo( 2867 """ 2868 |package com.squareup.tacos 2869 | 2870 |import kotlin.Number 2871 | 2872 |public class Location<T, P : Number> 2873 | 2874 """.trimMargin(), 2875 ) 2876 } 2877 multipleTypeAdditionnull2878 @Test fun multipleTypeAddition() { 2879 val taco = TypeSpec.classBuilder("Taco") 2880 .addTypes( 2881 listOf( 2882 TypeSpec.classBuilder("Topping").build(), 2883 TypeSpec.classBuilder("Sauce").build(), 2884 ), 2885 ) 2886 .build() 2887 assertThat(toString(taco)).isEqualTo( 2888 """ 2889 |package com.squareup.tacos 2890 | 2891 |public class Taco { 2892 | public class Topping 2893 | 2894 | public class Sauce 2895 |} 2896 | 2897 """.trimMargin(), 2898 ) 2899 } 2900 tryCatchnull2901 @Test fun tryCatch() { 2902 val taco = TypeSpec.classBuilder("Taco") 2903 .addFunction( 2904 FunSpec.builder("addTopping") 2905 .addParameter("topping", ClassName("com.squareup.tacos", "Topping")) 2906 .beginControlFlow("try") 2907 .addCode("/* do something tricky with the topping */\n") 2908 .nextControlFlow( 2909 "catch (e: %T)", 2910 ClassName("com.squareup.tacos", "IllegalToppingException"), 2911 ) 2912 .endControlFlow() 2913 .build(), 2914 ) 2915 .build() 2916 assertThat(toString(taco)).isEqualTo( 2917 """ 2918 |package com.squareup.tacos 2919 | 2920 |public class Taco { 2921 | public fun addTopping(topping: Topping) { 2922 | try { 2923 | /* do something tricky with the topping */ 2924 | } catch (e: IllegalToppingException) { 2925 | } 2926 | } 2927 |} 2928 | 2929 """.trimMargin(), 2930 ) 2931 } 2932 ifElsenull2933 @Test fun ifElse() { 2934 val taco = TypeSpec.classBuilder("Taco") 2935 .addFunction( 2936 FunSpec.builder("isDelicious") 2937 .addParameter("count", INT) 2938 .returns(BOOLEAN) 2939 .beginControlFlow("if (count > 0)") 2940 .addStatement("return true") 2941 .nextControlFlow("else") 2942 .addStatement("return false") 2943 .endControlFlow() 2944 .build(), 2945 ) 2946 .build() 2947 assertThat(toString(taco)).isEqualTo( 2948 """ 2949 |package com.squareup.tacos 2950 | 2951 |import kotlin.Boolean 2952 |import kotlin.Int 2953 | 2954 |public class Taco { 2955 | public fun isDelicious(count: Int): Boolean { 2956 | if (count > 0) { 2957 | return true 2958 | } else { 2959 | return false 2960 | } 2961 | } 2962 |} 2963 | 2964 """.trimMargin(), 2965 ) 2966 } 2967 whenReturnnull2968 @Test fun whenReturn() { 2969 val taco = TypeSpec.classBuilder("Taco") 2970 .addFunction( 2971 FunSpec.builder("toppingPrice") 2972 .addParameter("topping", String::class) 2973 .returns(INT) 2974 .beginControlFlow("return when(topping)") 2975 .addStatement("%S -> 1", "beef") 2976 .addStatement("%S -> 2", "lettuce") 2977 .addStatement("%S -> 3", "cheese") 2978 .addStatement("else -> throw IllegalToppingException(topping)") 2979 .endControlFlow() 2980 .build(), 2981 ) 2982 .build() 2983 assertThat(toString(taco)).isEqualTo( 2984 """ 2985 |package com.squareup.tacos 2986 | 2987 |import kotlin.Int 2988 |import kotlin.String 2989 | 2990 |public class Taco { 2991 | public fun toppingPrice(topping: String): Int = when(topping) { 2992 | "beef" -> 1 2993 | "lettuce" -> 2 2994 | "cheese" -> 3 2995 | else -> throw IllegalToppingException(topping) 2996 | } 2997 |} 2998 | 2999 """.trimMargin(), 3000 ) 3001 } 3002 literalFromAnythingnull3003 @Test fun literalFromAnything() { 3004 val value = object : Any() { 3005 override fun toString(): String { 3006 return "foo" 3007 } 3008 } 3009 assertThat(CodeBlock.of("%L", value).toString()).isEqualTo("foo") 3010 } 3011 nameFromCharSequencenull3012 @Test fun nameFromCharSequence() { 3013 assertThat(CodeBlock.of("%N", "text").toString()).isEqualTo("text") 3014 } 3015 nameFromPropertynull3016 @Test fun nameFromProperty() { 3017 val property = PropertySpec.builder("property", String::class).build() 3018 assertThat(CodeBlock.of("%N", property).toString()).isEqualTo("`property`") 3019 } 3020 nameFromParameternull3021 @Test fun nameFromParameter() { 3022 val parameter = ParameterSpec.builder("parameter", String::class).build() 3023 assertThat(CodeBlock.of("%N", parameter).toString()).isEqualTo("parameter") 3024 } 3025 nameFromFunctionnull3026 @Test fun nameFromFunction() { 3027 val funSpec = FunSpec.builder("method") 3028 .addModifiers(KModifier.ABSTRACT) 3029 .returns(String::class) 3030 .build() 3031 assertThat(CodeBlock.of("%N", funSpec).toString()).isEqualTo("method") 3032 } 3033 nameFromTypenull3034 @Test fun nameFromType() { 3035 val type = TypeSpec.classBuilder("Type").build() 3036 assertThat(CodeBlock.of("%N", type).toString()).isEqualTo("Type") 3037 } 3038 nameFromUnsupportedTypenull3039 @Test fun nameFromUnsupportedType() { 3040 assertThrows<IllegalArgumentException> { 3041 CodeBlock.builder().add("%N", String::class) 3042 }.hasMessageThat().isEqualTo("expected name but was " + String::class) 3043 } 3044 stringFromAnythingnull3045 @Test fun stringFromAnything() { 3046 val value = object : Any() { 3047 override fun toString(): String { 3048 return "foo" 3049 } 3050 } 3051 assertThat(CodeBlock.of("%S", value).toString()).isEqualTo("\"foo\"") 3052 } 3053 stringFromNullnull3054 @Test fun stringFromNull() { 3055 assertThat(CodeBlock.of("%S", null).toString()).isEqualTo("null") 3056 } 3057 typeFromTypeNamenull3058 @Test fun typeFromTypeName() { 3059 val typeName = String::class.asTypeName() 3060 assertThat(CodeBlock.of("%T", typeName).toString()).isEqualTo("kotlin.String") 3061 } 3062 typeFromTypeMirrornull3063 @Test fun typeFromTypeMirror() { 3064 val mirror = getElement(String::class).asType() 3065 assertThat(CodeBlock.of("%T", mirror).toString()).isEqualTo("java.lang.String") 3066 } 3067 typeFromTypeElementnull3068 @Test fun typeFromTypeElement() { 3069 val element = getElement(String::class) 3070 assertThat(CodeBlock.of("%T", element).toString()).isEqualTo("java.lang.String") 3071 } 3072 typeFromReflectTypenull3073 @Test fun typeFromReflectType() { 3074 assertThat(CodeBlock.of("%T", String::class).toString()).isEqualTo("kotlin.String") 3075 } 3076 typeFromUnsupportedTypenull3077 @Test fun typeFromUnsupportedType() { 3078 assertThrows<IllegalArgumentException> { 3079 CodeBlock.builder().add("%T", "kotlin.String") 3080 }.hasMessageThat().isEqualTo("expected type but was kotlin.String") 3081 } 3082 tooFewArgumentsnull3083 @Test fun tooFewArguments() { 3084 assertThrows<IllegalArgumentException> { 3085 CodeBlock.builder().add("%S") 3086 }.hasMessageThat().isEqualTo("index 1 for '%S' not in range (received 0 arguments)") 3087 } 3088 unusedArgumentsRelativenull3089 @Test fun unusedArgumentsRelative() { 3090 assertThrows<IllegalArgumentException> { 3091 CodeBlock.builder().add("%L %L", "a", "b", "c") 3092 }.hasMessageThat().isEqualTo("unused arguments: expected 2, received 3") 3093 } 3094 unusedArgumentsIndexednull3095 @Test fun unusedArgumentsIndexed() { 3096 assertThrows<IllegalArgumentException> { 3097 CodeBlock.builder().add("%1L %2L", "a", "b", "c") 3098 }.hasMessageThat().isEqualTo("unused argument: %3") 3099 3100 assertThrows<IllegalArgumentException> { 3101 CodeBlock.builder().add("%1L %1L %1L", "a", "b", "c") 3102 }.hasMessageThat().isEqualTo("unused arguments: %2, %3") 3103 3104 assertThrows<IllegalArgumentException> { 3105 CodeBlock.builder().add("%3L %1L %3L %1L %3L", "a", "b", "c", "d") 3106 }.hasMessageThat().isEqualTo("unused arguments: %2, %4") 3107 } 3108 superClassOnlyValidForClassesnull3109 @Test fun superClassOnlyValidForClasses() { 3110 assertThrows<IllegalStateException> { 3111 TypeSpec.annotationBuilder("A").superclass(Any::class.asClassName()) 3112 } 3113 3114 assertThrows<IllegalStateException> { 3115 TypeSpec.enumBuilder("E").superclass(Any::class.asClassName()) 3116 } 3117 3118 assertThrows<IllegalStateException> { 3119 TypeSpec.interfaceBuilder("I").superclass(Any::class.asClassName()) 3120 } 3121 } 3122 superClassConstructorParametersOnlyValidForClassesnull3123 @Test fun superClassConstructorParametersOnlyValidForClasses() { 3124 assertThrows<IllegalStateException> { 3125 TypeSpec.annotationBuilder("A").addSuperclassConstructorParameter("") 3126 } 3127 3128 assertThrows<IllegalStateException> { 3129 TypeSpec.enumBuilder("E").addSuperclassConstructorParameter("") 3130 } 3131 3132 assertThrows<IllegalStateException> { 3133 TypeSpec.interfaceBuilder("I").addSuperclassConstructorParameter("") 3134 } 3135 } 3136 anonymousClassesCannotHaveModifiersOrTypeVariablenull3137 @Test fun anonymousClassesCannotHaveModifiersOrTypeVariable() { 3138 assertThrows<IllegalStateException> { 3139 TypeSpec.anonymousClassBuilder().addModifiers(PUBLIC) 3140 } 3141 3142 assertThrows<IllegalStateException> { 3143 TypeSpec.anonymousClassBuilder().addTypeVariable(TypeVariableName("T")).build() 3144 } 3145 3146 assertThrows<IllegalStateException> { 3147 TypeSpec.anonymousClassBuilder().addTypeVariables(listOf(TypeVariableName("T"))).build() 3148 } 3149 } 3150 invalidSuperClassnull3151 @Test fun invalidSuperClass() { 3152 assertThrows<IllegalStateException> { 3153 TypeSpec.classBuilder("foo") 3154 .superclass(List::class) 3155 .superclass(Map::class) 3156 } 3157 } 3158 staticCodeBlocknull3159 @Test fun staticCodeBlock() { 3160 val taco = TypeSpec.classBuilder("Taco") 3161 .addProperty("foo", String::class, KModifier.PRIVATE) 3162 .addProperty( 3163 PropertySpec.builder("FOO", String::class, KModifier.PRIVATE, KModifier.CONST) 3164 .initializer("%S", "FOO") 3165 .build(), 3166 ) 3167 .addFunction( 3168 FunSpec.builder("toString") 3169 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 3170 .returns(String::class) 3171 .addStatement("return FOO") 3172 .build(), 3173 ) 3174 .build() 3175 assertThat(toString(taco)).isEqualTo( 3176 """ 3177 |package com.squareup.tacos 3178 | 3179 |import kotlin.String 3180 | 3181 |public class Taco { 3182 | private val foo: String 3183 | 3184 | private const val FOO: String = "FOO" 3185 | 3186 | public override fun toString(): String = FOO 3187 |} 3188 | 3189 """.trimMargin(), 3190 ) 3191 } 3192 initializerBlockInRightPlacenull3193 @Test fun initializerBlockInRightPlace() { 3194 val taco = TypeSpec.classBuilder("Taco") 3195 .addProperty("foo", String::class, KModifier.PRIVATE) 3196 .addProperty( 3197 PropertySpec.builder("FOO", String::class, KModifier.PRIVATE, KModifier.CONST) 3198 .initializer("%S", "FOO") 3199 .build(), 3200 ) 3201 .addFunction(FunSpec.constructorBuilder().build()) 3202 .addFunction( 3203 FunSpec.builder("toString") 3204 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 3205 .returns(String::class) 3206 .addStatement("return FOO") 3207 .build(), 3208 ) 3209 .addInitializerBlock( 3210 CodeBlock.builder() 3211 .addStatement("foo = %S", "FOO") 3212 .build(), 3213 ) 3214 .build() 3215 assertThat(toString(taco)).isEqualTo( 3216 """ 3217 |package com.squareup.tacos 3218 | 3219 |import kotlin.String 3220 | 3221 |public class Taco { 3222 | private val foo: String 3223 | 3224 | private const val FOO: String = "FOO" 3225 | 3226 | init { 3227 | foo = "FOO" 3228 | } 3229 | 3230 | public constructor() 3231 | 3232 | public override fun toString(): String = FOO 3233 |} 3234 | 3235 """.trimMargin(), 3236 ) 3237 } 3238 initializersToBuildernull3239 @Test fun initializersToBuilder() { 3240 // Tests if toBuilder() contains instance initializers 3241 val taco = TypeSpec.classBuilder("Taco") 3242 .addProperty(PropertySpec.builder("foo", String::class, KModifier.PRIVATE).build()) 3243 .addProperty( 3244 PropertySpec.builder("FOO", String::class, KModifier.PRIVATE, KModifier.CONST) 3245 .initializer("%S", "FOO") 3246 .build(), 3247 ) 3248 .addFunction(FunSpec.constructorBuilder().build()) 3249 .addFunction( 3250 FunSpec.builder("toString") 3251 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 3252 .returns(String::class) 3253 .addStatement("return FOO") 3254 .build(), 3255 ) 3256 .build() 3257 3258 val recreatedTaco = taco.toBuilder().build() 3259 assertThat(toString(taco)).isEqualTo(toString(recreatedTaco)) 3260 3261 val initializersAdded = taco.toBuilder() 3262 .addInitializerBlock( 3263 CodeBlock.builder() 3264 .addStatement("foo = %S", "instanceFoo") 3265 .build(), 3266 ) 3267 .build() 3268 3269 assertThat(toString(initializersAdded)).isEqualTo( 3270 """ 3271 |package com.squareup.tacos 3272 | 3273 |import kotlin.String 3274 | 3275 |public class Taco { 3276 | private val foo: String 3277 | 3278 | private const val FOO: String = "FOO" 3279 | 3280 | init { 3281 | foo = "instanceFoo" 3282 | } 3283 | 3284 | public constructor() 3285 | 3286 | public override fun toString(): String = FOO 3287 |} 3288 | 3289 """.trimMargin(), 3290 ) 3291 } 3292 generalToBuilderEqualityTestnull3293 @Test fun generalToBuilderEqualityTest() { 3294 val originatingElement = FakeElement() 3295 val comprehensiveTaco = TypeSpec.classBuilder("Taco") 3296 .addKdoc("SuperTaco") 3297 .addAnnotation(SuppressWarnings::class) 3298 .addModifiers(DATA) 3299 .addTypeVariable(TypeVariableName.of("State", listOf(ANY), IN).copy(reified = true)) 3300 .addType( 3301 TypeSpec.companionObjectBuilder() 3302 .build(), 3303 ) 3304 .addType( 3305 TypeSpec.classBuilder("InnerTaco") 3306 .addModifiers(INNER) 3307 .build(), 3308 ) 3309 .primaryConstructor( 3310 FunSpec.constructorBuilder() 3311 .build(), 3312 ) 3313 .superclass(ClassName("texmexfood", "TortillaBased")) 3314 .addSuperclassConstructorParameter("true") 3315 .addProperty( 3316 PropertySpec.builder("meat", ClassName("texmexfood", "Meat")) 3317 .build(), 3318 ) 3319 .addFunction( 3320 FunSpec.builder("fold") 3321 .build(), 3322 ) 3323 .addSuperinterface(ClassName("texmexfood", "Consumable")) 3324 .addOriginatingElement(originatingElement) 3325 .build() 3326 3327 val newTaco = comprehensiveTaco.toBuilder().build() 3328 assertThat(newTaco).isEqualTo(comprehensiveTaco) 3329 assertThat(newTaco.originatingElements).containsExactly(originatingElement) 3330 } 3331 generalEnumToBuilderEqualityTestnull3332 @Test fun generalEnumToBuilderEqualityTest() { 3333 val bestTexMexEnum = TypeSpec.enumBuilder("BestTexMex") 3334 .addEnumConstant("TACO") 3335 .addEnumConstant("BREAKFAST_TACO") 3336 .build() 3337 3338 assertThat(bestTexMexEnum.toBuilder().build()).isEqualTo(bestTexMexEnum) 3339 } 3340 generalInterfaceBuilderEqualityTestnull3341 @Test fun generalInterfaceBuilderEqualityTest() { 3342 val taco = TypeSpec.interfaceBuilder("Taco") 3343 .addProperty("isVegan", Boolean::class) 3344 .addSuperinterface(Runnable::class) 3345 .build() 3346 assertThat(taco.toBuilder().build()).isEqualTo(taco) 3347 } 3348 generalAnnotationBuilderEqualityTestnull3349 @Test fun generalAnnotationBuilderEqualityTest() { 3350 val annotation = TypeSpec.annotationBuilder("MyAnnotation") 3351 .addModifiers(KModifier.PUBLIC) 3352 .primaryConstructor( 3353 FunSpec.constructorBuilder() 3354 .addParameter( 3355 ParameterSpec.builder("test", Int::class) 3356 .build(), 3357 ) 3358 .build(), 3359 ) 3360 .addProperty( 3361 PropertySpec.builder("test", Int::class) 3362 .initializer("test") 3363 .build(), 3364 ) 3365 .build() 3366 assertThat(annotation.toBuilder().build()).isEqualTo(annotation) 3367 } 3368 generalExpectClassBuilderEqualityTestnull3369 @Test fun generalExpectClassBuilderEqualityTest() { 3370 val expectSpec = TypeSpec.classBuilder("AtmoicRef") 3371 .addModifiers(KModifier.EXPECT, KModifier.INTERNAL) 3372 .primaryConstructor( 3373 FunSpec.constructorBuilder() 3374 .addParameter("value", Int::class) 3375 .build(), 3376 ) 3377 .addProperty(PropertySpec.builder("value", Int::class).build()) 3378 .addFunction( 3379 FunSpec.builder("get") 3380 .returns(Int::class) 3381 .build(), 3382 ) 3383 .build() 3384 assertThat(expectSpec.toBuilder().build()).isEqualTo(expectSpec) 3385 } 3386 generalObjectBuilderEqualityTestnull3387 @Test fun generalObjectBuilderEqualityTest() { 3388 val objectSpec = TypeSpec.objectBuilder("MyObject") 3389 .addModifiers(KModifier.PUBLIC) 3390 .addProperty("tacos", Int::class) 3391 .addInitializerBlock(CodeBlock.builder().build()) 3392 .addFunction( 3393 FunSpec.builder("test") 3394 .addModifiers(KModifier.PUBLIC) 3395 .build(), 3396 ) 3397 .build() 3398 assertThat(objectSpec.toBuilder().build()).isEqualTo(objectSpec) 3399 } 3400 generalAnonymousClassBuilderEqualityTestnull3401 @Test fun generalAnonymousClassBuilderEqualityTest() { 3402 val anonObjectSpec = TypeSpec.anonymousClassBuilder() 3403 .addSuperinterface(Runnable::class) 3404 .addFunction( 3405 FunSpec.builder("run") 3406 .addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE) 3407 .build(), 3408 ) 3409 .build() 3410 assertThat(anonObjectSpec.toBuilder().build()).isEqualTo(anonObjectSpec) 3411 } 3412 initializerBlockUnsupportedExceptionOnInterfacenull3413 @Test fun initializerBlockUnsupportedExceptionOnInterface() { 3414 val interfaceBuilder = TypeSpec.interfaceBuilder("Taco") 3415 assertThrows<IllegalStateException> { 3416 interfaceBuilder.addInitializerBlock(CodeBlock.builder().build()) 3417 } 3418 } 3419 initializerBlockUnsupportedExceptionOnAnnotationnull3420 @Test fun initializerBlockUnsupportedExceptionOnAnnotation() { 3421 val annotationBuilder = TypeSpec.annotationBuilder("Taco") 3422 assertThrows<IllegalStateException> { 3423 annotationBuilder.addInitializerBlock(CodeBlock.builder().build()) 3424 } 3425 } 3426 equalsAndHashCodenull3427 @Test fun equalsAndHashCode() { 3428 var a = TypeSpec.interfaceBuilder("taco").build() 3429 var b = TypeSpec.interfaceBuilder("taco").build() 3430 assertThat(a == b).isTrue() 3431 assertThat(a.hashCode()).isEqualTo(b.hashCode()) 3432 a = TypeSpec.classBuilder("taco").build() 3433 b = TypeSpec.classBuilder("taco").build() 3434 assertThat(a == b).isTrue() 3435 assertThat(a.hashCode()).isEqualTo(b.hashCode()) 3436 a = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build() 3437 b = TypeSpec.enumBuilder("taco").addEnumConstant("SALSA").build() 3438 assertThat(a == b).isTrue() 3439 assertThat(a.hashCode()).isEqualTo(b.hashCode()) 3440 a = TypeSpec.annotationBuilder("taco").build() 3441 b = TypeSpec.annotationBuilder("taco").build() 3442 assertThat(a == b).isTrue() 3443 assertThat(a.hashCode()).isEqualTo(b.hashCode()) 3444 } 3445 classNameFactoriesnull3446 @Test fun classNameFactories() { 3447 val className = ClassName("com.example", "Example") 3448 assertThat(TypeSpec.classBuilder(className).build().name).isEqualTo("Example") 3449 assertThat(TypeSpec.interfaceBuilder(className).build().name).isEqualTo("Example") 3450 assertThat(TypeSpec.enumBuilder(className).addEnumConstant("A").build().name).isEqualTo("Example") 3451 assertThat(TypeSpec.annotationBuilder(className).build().name).isEqualTo("Example") 3452 } 3453 objectTypenull3454 @Test fun objectType() { 3455 val type = TypeSpec.objectBuilder("MyObject") 3456 .addModifiers(KModifier.PUBLIC) 3457 .addProperty("tacos", Int::class) 3458 .addInitializerBlock(CodeBlock.builder().build()) 3459 .addFunction( 3460 FunSpec.builder("test") 3461 .addModifiers(KModifier.PUBLIC) 3462 .build(), 3463 ) 3464 .build() 3465 3466 assertThat(toString(type)).isEqualTo( 3467 """ 3468 |package com.squareup.tacos 3469 | 3470 |import kotlin.Int 3471 | 3472 |public object MyObject { 3473 | public val tacos: Int 3474 | 3475 | init { 3476 | } 3477 | 3478 | public fun test() { 3479 | } 3480 |} 3481 | 3482 """.trimMargin(), 3483 ) 3484 } 3485 objectClassWithSupertypenull3486 @Test fun objectClassWithSupertype() { 3487 val superclass = ClassName("com.squareup.wire", "Message") 3488 val type = TypeSpec.objectBuilder("MyObject") 3489 .addModifiers(KModifier.PUBLIC) 3490 .superclass(superclass) 3491 .addInitializerBlock(CodeBlock.builder().build()) 3492 .addFunction( 3493 FunSpec.builder("test") 3494 .addModifiers(KModifier.PUBLIC) 3495 .build(), 3496 ) 3497 .build() 3498 3499 assertThat(toString(type)).isEqualTo( 3500 """ 3501 |package com.squareup.tacos 3502 | 3503 |import com.squareup.wire.Message 3504 | 3505 |public object MyObject : Message() { 3506 | init { 3507 | } 3508 | 3509 | public fun test() { 3510 | } 3511 |} 3512 | 3513 """.trimMargin(), 3514 ) 3515 } 3516 companionObjectnull3517 @Test fun companionObject() { 3518 val companion = TypeSpec.companionObjectBuilder() 3519 .addProperty( 3520 PropertySpec.builder("tacos", Int::class) 3521 .initializer("%L", 42) 3522 .build(), 3523 ) 3524 .addFunction( 3525 FunSpec.builder("test") 3526 .addModifiers(KModifier.PUBLIC) 3527 .build(), 3528 ) 3529 .build() 3530 3531 val type = TypeSpec.classBuilder("MyClass") 3532 .addModifiers(KModifier.PUBLIC) 3533 .addType(companion) 3534 .build() 3535 3536 assertThat(toString(type)).isEqualTo( 3537 """ 3538 |package com.squareup.tacos 3539 | 3540 |import kotlin.Int 3541 | 3542 |public class MyClass { 3543 | public companion object { 3544 | public val tacos: Int = 42 3545 | 3546 | public fun test() { 3547 | } 3548 | } 3549 |} 3550 | 3551 """.trimMargin(), 3552 ) 3553 } 3554 companionObjectWithInitializernull3555 @Test fun companionObjectWithInitializer() { 3556 val companion = TypeSpec.companionObjectBuilder() 3557 .addProperty( 3558 PropertySpec.builder("tacos", Int::class) 3559 .mutable() 3560 .initializer("%L", 24) 3561 .build(), 3562 ) 3563 .addInitializerBlock( 3564 CodeBlock.builder() 3565 .addStatement("tacos = %L", 42) 3566 .build(), 3567 ) 3568 .build() 3569 3570 val type = TypeSpec.classBuilder("MyClass") 3571 .addModifiers(KModifier.PUBLIC) 3572 .addType(companion) 3573 .build() 3574 3575 assertThat(toString(type)).isEqualTo( 3576 """ 3577 |package com.squareup.tacos 3578 | 3579 |import kotlin.Int 3580 | 3581 |public class MyClass { 3582 | public companion object { 3583 | public var tacos: Int = 24 3584 | 3585 | init { 3586 | tacos = 42 3587 | } 3588 | } 3589 |} 3590 | 3591 """.trimMargin(), 3592 ) 3593 } 3594 companionObjectWithNamenull3595 @Test fun companionObjectWithName() { 3596 val companion = TypeSpec.companionObjectBuilder("Factory") 3597 .addFunction(FunSpec.builder("tacos").build()) 3598 .build() 3599 3600 val type = TypeSpec.classBuilder("MyClass") 3601 .addType(companion) 3602 .build() 3603 3604 assertThat(toString(type)).isEqualTo( 3605 """ 3606 |package com.squareup.tacos 3607 | 3608 |public class MyClass { 3609 | public companion object Factory { 3610 | public fun tacos() { 3611 | } 3612 | } 3613 |} 3614 | 3615 """.trimMargin(), 3616 ) 3617 } 3618 companionObjectOnInterfacenull3619 @Test fun companionObjectOnInterface() { 3620 val companion = TypeSpec.companionObjectBuilder() 3621 .addFunction( 3622 FunSpec.builder("test") 3623 .addModifiers(KModifier.PUBLIC) 3624 .build(), 3625 ) 3626 .build() 3627 3628 val type = TypeSpec.interfaceBuilder("MyInterface") 3629 .addModifiers(KModifier.PUBLIC) 3630 .addType(companion) 3631 .build() 3632 3633 assertThat(toString(type)).isEqualTo( 3634 """ 3635 |package com.squareup.tacos 3636 | 3637 |public interface MyInterface { 3638 | public companion object { 3639 | public fun test() { 3640 | } 3641 | } 3642 |} 3643 | 3644 """.trimMargin(), 3645 ) 3646 } 3647 companionObjectOnEnumnull3648 @Test fun companionObjectOnEnum() { 3649 val companion = TypeSpec.companionObjectBuilder() 3650 .addFunction( 3651 FunSpec.builder("test") 3652 .addModifiers(KModifier.PUBLIC) 3653 .build(), 3654 ) 3655 .build() 3656 3657 val enumBuilder = TypeSpec.enumBuilder("MyEnum") 3658 .addEnumConstant("FOO") 3659 .addEnumConstant("BAR") 3660 .addModifiers(KModifier.PUBLIC) 3661 .addType(companion) 3662 .build() 3663 3664 assertThat(toString(enumBuilder)).isEqualTo( 3665 """ 3666 |package com.squareup.tacos 3667 | 3668 |public enum class MyEnum { 3669 | FOO, 3670 | BAR, 3671 | ; 3672 | 3673 | public companion object { 3674 | public fun test() { 3675 | } 3676 | } 3677 |} 3678 | 3679 """.trimMargin(), 3680 ) 3681 } 3682 companionObjectOnObjectNotAllowednull3683 @Test fun companionObjectOnObjectNotAllowed() { 3684 val companion = TypeSpec.companionObjectBuilder() 3685 .addFunction( 3686 FunSpec.builder("test") 3687 .addModifiers(KModifier.PUBLIC) 3688 .build(), 3689 ) 3690 .build() 3691 3692 val objectBuilder = TypeSpec.objectBuilder("MyObject") 3693 .addModifiers(KModifier.PUBLIC) 3694 .addType(companion) 3695 3696 assertThrows<IllegalArgumentException> { 3697 objectBuilder.build() 3698 } 3699 } 3700 companionObjectSupernull3701 @Test fun companionObjectSuper() { 3702 val superclass = ClassName("com.squareup.wire", "Message") 3703 val companion = TypeSpec.companionObjectBuilder() 3704 .superclass(superclass) 3705 .addFunction( 3706 FunSpec.builder("test") 3707 .addModifiers(KModifier.PUBLIC) 3708 .build(), 3709 ) 3710 .build() 3711 3712 val type = TypeSpec.classBuilder("MyClass") 3713 .addModifiers(KModifier.PUBLIC) 3714 .addType(companion) 3715 .build() 3716 3717 assertThat(toString(type)).isEqualTo( 3718 """ 3719 |package com.squareup.tacos 3720 | 3721 |import com.squareup.wire.Message 3722 | 3723 |public class MyClass { 3724 | public companion object : Message() { 3725 | public fun test() { 3726 | } 3727 | } 3728 |} 3729 | 3730 """.trimMargin(), 3731 ) 3732 } 3733 propertyInPrimaryConstructornull3734 @Test fun propertyInPrimaryConstructor() { 3735 val type = TypeSpec.classBuilder("Taco") 3736 .primaryConstructor( 3737 FunSpec.constructorBuilder() 3738 .addParameter("a", Int::class) 3739 .addParameter("b", String::class) 3740 .build(), 3741 ) 3742 .addProperty( 3743 PropertySpec.builder("a", Int::class) 3744 .initializer("a") 3745 .build(), 3746 ) 3747 .addProperty( 3748 PropertySpec.builder("b", String::class) 3749 .initializer("b") 3750 .build(), 3751 ) 3752 .build() 3753 3754 assertThat(toString(type)).isEqualTo( 3755 """ 3756 |package com.squareup.tacos 3757 | 3758 |import kotlin.Int 3759 |import kotlin.String 3760 | 3761 |public class Taco( 3762 | public val a: Int, 3763 | public val b: String, 3764 |) 3765 | 3766 """.trimMargin(), 3767 ) 3768 } 3769 propertyWithKdocInPrimaryConstructornull3770 @Test fun propertyWithKdocInPrimaryConstructor() { 3771 val type = TypeSpec.classBuilder("Taco") 3772 .primaryConstructor( 3773 FunSpec.constructorBuilder() 3774 .addParameter("a", Int::class) 3775 .addParameter("b", String::class) 3776 .build(), 3777 ) 3778 .addProperty( 3779 PropertySpec.builder("a", Int::class) 3780 .initializer("a") 3781 .addKdoc("KDoc\n") 3782 .build(), 3783 ) 3784 .addProperty( 3785 PropertySpec.builder("b", String::class) 3786 .initializer("b") 3787 .build(), 3788 ) 3789 .build() 3790 3791 assertThat(toString(type)).isEqualTo( 3792 """ 3793 |package com.squareup.tacos 3794 | 3795 |import kotlin.Int 3796 |import kotlin.String 3797 | 3798 |public class Taco( 3799 | /** 3800 | * KDoc 3801 | */ 3802 | public val a: Int, 3803 | public val b: String, 3804 |) 3805 | 3806 """.trimMargin(), 3807 ) 3808 } 3809 annotatedConstructornull3810 @Test fun annotatedConstructor() { 3811 val injectAnnotation = ClassName("javax.inject", "Inject") 3812 val taco = TypeSpec.classBuilder("Taco") 3813 .primaryConstructor( 3814 FunSpec.constructorBuilder() 3815 .addAnnotation(AnnotationSpec.builder(injectAnnotation).build()) 3816 .build(), 3817 ) 3818 .build() 3819 3820 assertThat(toString(taco)).isEqualTo( 3821 """ 3822 |package com.squareup.tacos 3823 | 3824 |import javax.inject.Inject 3825 | 3826 |public class Taco @Inject constructor() 3827 | 3828 """.trimMargin(), 3829 ) 3830 } 3831 internalConstructornull3832 @Test fun internalConstructor() { 3833 val taco = TypeSpec.classBuilder("Taco") 3834 .primaryConstructor( 3835 FunSpec.constructorBuilder() 3836 .addModifiers(INTERNAL) 3837 .build(), 3838 ) 3839 .build() 3840 3841 assertThat(toString(taco)).isEqualTo( 3842 """ 3843 |package com.squareup.tacos 3844 | 3845 |public class Taco internal constructor() 3846 | 3847 """.trimMargin(), 3848 ) 3849 } 3850 annotatedInternalConstructornull3851 @Test fun annotatedInternalConstructor() { 3852 val injectAnnotation = ClassName("javax.inject", "Inject") 3853 val taco = TypeSpec.classBuilder("Taco") 3854 .primaryConstructor( 3855 FunSpec.constructorBuilder() 3856 .addAnnotation(AnnotationSpec.builder(injectAnnotation).build()) 3857 .addModifiers(INTERNAL) 3858 .build(), 3859 ) 3860 .build() 3861 3862 assertThat(toString(taco)).isEqualTo( 3863 """ 3864 |package com.squareup.tacos 3865 | 3866 |import javax.inject.Inject 3867 | 3868 |public class Taco @Inject internal constructor() 3869 | 3870 """.trimMargin(), 3871 ) 3872 } 3873 multipleAnnotationsInternalConstructornull3874 @Test fun multipleAnnotationsInternalConstructor() { 3875 val injectAnnotation = ClassName("javax.inject", "Inject") 3876 val namedAnnotation = ClassName("javax.inject", "Named") 3877 val taco = TypeSpec.classBuilder("Taco") 3878 .primaryConstructor( 3879 FunSpec.constructorBuilder() 3880 .addAnnotation(AnnotationSpec.builder(injectAnnotation).build()) 3881 .addAnnotation(AnnotationSpec.builder(namedAnnotation).build()) 3882 .addModifiers(INTERNAL) 3883 .build(), 3884 ) 3885 .build() 3886 3887 assertThat(toString(taco)).isEqualTo( 3888 """ 3889 |package com.squareup.tacos 3890 | 3891 |import javax.inject.Inject 3892 |import javax.inject.Named 3893 | 3894 |public class Taco @Inject @Named internal constructor() 3895 | 3896 """.trimMargin(), 3897 ) 3898 } 3899 importNonNullablePropertynull3900 @Test fun importNonNullableProperty() { 3901 val type = String::class.asTypeName() 3902 val taco = TypeSpec.classBuilder("Taco") 3903 .addProperty( 3904 PropertySpec.builder("taco", type.copy(nullable = false)) 3905 .initializer("%S", "taco") 3906 .build(), 3907 ) 3908 .addProperty( 3909 PropertySpec.builder("nullTaco", type.copy(nullable = true)) 3910 .initializer("null") 3911 .build(), 3912 ) 3913 .build() 3914 3915 assertThat(toString(taco)).isEqualTo( 3916 """ 3917 |package com.squareup.tacos 3918 | 3919 |import kotlin.String 3920 | 3921 |public class Taco { 3922 | public val taco: String = "taco" 3923 | 3924 | public val nullTaco: String? = null 3925 |} 3926 | 3927 """.trimMargin(), 3928 ) 3929 } 3930 superclassConstructorParamsnull3931 @Test fun superclassConstructorParams() { 3932 val taco = TypeSpec.classBuilder("Foo") 3933 .superclass(ClassName(tacosPackage, "Bar")) 3934 .addSuperclassConstructorParameter("%S", "foo") 3935 .addSuperclassConstructorParameter(CodeBlock.of("%L", 42)) 3936 .build() 3937 3938 assertThat(toString(taco)).isEqualTo( 3939 """ 3940 |package com.squareup.tacos 3941 | 3942 |public class Foo : Bar("foo", 42) 3943 | 3944 """.trimMargin(), 3945 ) 3946 } 3947 superclassConstructorParamsForbiddenForAnnotationnull3948 @Test fun superclassConstructorParamsForbiddenForAnnotation() { 3949 assertThrows<IllegalStateException> { 3950 TypeSpec.annotationBuilder("Taco") 3951 .addSuperclassConstructorParameter("%S", "foo") 3952 } 3953 } 3954 classExtendsNoPrimaryConstructornull3955 @Test fun classExtendsNoPrimaryConstructor() { 3956 val typeSpec = TypeSpec.classBuilder("IoException") 3957 .superclass(Exception::class) 3958 .addFunction(FunSpec.constructorBuilder().build()) 3959 .build() 3960 3961 assertThat(toString(typeSpec)).isEqualTo( 3962 """ 3963 |package com.squareup.tacos 3964 | 3965 |import java.lang.Exception 3966 | 3967 |public class IoException : Exception { 3968 | public constructor() 3969 |} 3970 | 3971 """.trimMargin(), 3972 ) 3973 } 3974 classExtendsNoPrimaryOrSecondaryConstructornull3975 @Test fun classExtendsNoPrimaryOrSecondaryConstructor() { 3976 val typeSpec = TypeSpec.classBuilder("IoException") 3977 .superclass(Exception::class) 3978 .build() 3979 3980 assertThat(toString(typeSpec)).isEqualTo( 3981 """ 3982 |package com.squareup.tacos 3983 | 3984 |import java.lang.Exception 3985 | 3986 |public class IoException : Exception() 3987 | 3988 """.trimMargin(), 3989 ) 3990 } 3991 classExtendsNoPrimaryConstructorButSuperclassParamsnull3992 @Test fun classExtendsNoPrimaryConstructorButSuperclassParams() { 3993 assertThrows<IllegalArgumentException> { 3994 TypeSpec.classBuilder("IoException") 3995 .superclass(Exception::class) 3996 .addSuperclassConstructorParameter("%S", "hey") 3997 .addFunction(FunSpec.constructorBuilder().build()) 3998 .build() 3999 }.hasMessageThat().isEqualTo( 4000 "types without a primary constructor cannot specify secondary constructors and superclass constructor parameters", 4001 ) 4002 } 4003 constructorWithDefaultParamValuenull4004 @Test fun constructorWithDefaultParamValue() { 4005 val type = TypeSpec.classBuilder("Taco") 4006 .primaryConstructor( 4007 FunSpec.constructorBuilder() 4008 .addParameter( 4009 ParameterSpec.builder("a", Int::class) 4010 .defaultValue("1") 4011 .build(), 4012 ) 4013 .addParameter( 4014 ParameterSpec 4015 .builder("b", String::class.asTypeName().copy(nullable = true)) 4016 .defaultValue("null") 4017 .build(), 4018 ) 4019 .build(), 4020 ) 4021 .addProperty( 4022 PropertySpec.builder("a", Int::class) 4023 .initializer("a") 4024 .build(), 4025 ) 4026 .addProperty( 4027 PropertySpec.builder("b", String::class.asTypeName().copy(nullable = true)) 4028 .initializer("b") 4029 .build(), 4030 ) 4031 .build() 4032 4033 assertThat(toString(type)).isEqualTo( 4034 """ 4035 |package com.squareup.tacos 4036 | 4037 |import kotlin.Int 4038 |import kotlin.String 4039 | 4040 |public class Taco( 4041 | public val a: Int = 1, 4042 | public val b: String? = null, 4043 |) 4044 | 4045 """.trimMargin(), 4046 ) 4047 } 4048 constructorDelegationnull4049 @Test fun constructorDelegation() { 4050 val type = TypeSpec.classBuilder("Taco") 4051 .primaryConstructor( 4052 FunSpec.constructorBuilder() 4053 .addParameter("a", String::class.asTypeName().copy(nullable = true)) 4054 .addParameter("b", String::class.asTypeName().copy(nullable = true)) 4055 .addParameter("c", String::class.asTypeName().copy(nullable = true)) 4056 .build(), 4057 ) 4058 .addProperty( 4059 PropertySpec.builder("a", String::class.asTypeName().copy(nullable = true)) 4060 .initializer("a") 4061 .build(), 4062 ) 4063 .addProperty( 4064 PropertySpec.builder("b", String::class.asTypeName().copy(nullable = true)) 4065 .initializer("b") 4066 .build(), 4067 ) 4068 .addProperty( 4069 PropertySpec.builder("c", String::class.asTypeName().copy(nullable = true)) 4070 .initializer("c") 4071 .build(), 4072 ) 4073 .addFunction( 4074 FunSpec.constructorBuilder() 4075 .addParameter("map", Map::class.parameterizedBy(String::class, String::class)) 4076 .callThisConstructor( 4077 CodeBlock.of("map[%S]", "a"), 4078 CodeBlock.of("map[%S]", "b"), 4079 CodeBlock.of("map[%S]", "c"), 4080 ) 4081 .build(), 4082 ) 4083 .build() 4084 4085 assertThat(toString(type)).isEqualTo( 4086 """ 4087 |package com.squareup.tacos 4088 | 4089 |import kotlin.String 4090 |import kotlin.collections.Map 4091 | 4092 |public class Taco( 4093 | public val a: String?, 4094 | public val b: String?, 4095 | public val c: String?, 4096 |) { 4097 | public constructor(map: Map<String, String>) : this(map["a"], map["b"], map["c"]) 4098 |} 4099 | 4100 """.trimMargin(), 4101 ) 4102 } 4103 internalFunForbiddenInInterfacenull4104 @Test fun internalFunForbiddenInInterface() { 4105 val type = TypeSpec.interfaceBuilder("ITaco") 4106 4107 assertThrows<IllegalArgumentException> { 4108 type.addFunction( 4109 FunSpec.builder("eat") 4110 .addModifiers(ABSTRACT, INTERNAL) 4111 .build(), 4112 ) 4113 .build() 4114 }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, INTERNAL] must contain none of [INTERNAL, PROTECTED]") 4115 4116 assertThrows<IllegalArgumentException> { 4117 type.addFunctions( 4118 listOf( 4119 FunSpec.builder("eat") 4120 .addModifiers(ABSTRACT, INTERNAL) 4121 .build(), 4122 ), 4123 ) 4124 .build() 4125 }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, INTERNAL] must contain none of [INTERNAL, PROTECTED]") 4126 } 4127 privateAbstractFunForbiddenInInterfacenull4128 @Test fun privateAbstractFunForbiddenInInterface() { 4129 val type = TypeSpec.interfaceBuilder("ITaco") 4130 4131 assertThrows<IllegalArgumentException> { 4132 type.addFunction( 4133 FunSpec.builder("eat") 4134 .addModifiers(ABSTRACT, PRIVATE) 4135 .build(), 4136 ) 4137 .build() 4138 }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, PRIVATE] must contain none or only one of [ABSTRACT, PRIVATE]") 4139 4140 assertThrows<IllegalArgumentException> { 4141 type.addFunctions( 4142 listOf( 4143 FunSpec.builder("eat") 4144 .addModifiers(ABSTRACT, PRIVATE) 4145 .build(), 4146 ), 4147 ) 4148 .build() 4149 }.hasMessageThat().isEqualTo("modifiers [ABSTRACT, PRIVATE] must contain none or only one of [ABSTRACT, PRIVATE]") 4150 } 4151 internalConstructorForbiddenInAnnotationnull4152 @Test fun internalConstructorForbiddenInAnnotation() { 4153 val type = TypeSpec.annotationBuilder("Taco") 4154 4155 assertThrows<IllegalArgumentException> { 4156 type.primaryConstructor( 4157 FunSpec.constructorBuilder() 4158 .addModifiers(INTERNAL) 4159 .build(), 4160 ) 4161 .build() 4162 }.hasMessageThat().isEqualTo("modifiers [INTERNAL] must contain none of [INTERNAL, PROTECTED, PRIVATE, ABSTRACT]") 4163 } 4164 4165 // https://github.com/square/kotlinpoet/issues/1557 memberFunForbiddenInAnnotationnull4166 @Test fun memberFunForbiddenInAnnotation() { 4167 val type = TypeSpec.annotationBuilder("Taco") 4168 4169 assertThrows<IllegalArgumentException> { 4170 type.addFunction( 4171 FunSpec.builder("eat") 4172 .build(), 4173 ) 4174 .build() 4175 }.hasMessageThat().isEqualTo("annotation class Taco cannot declare member function eat") 4176 } 4177 4178 // https://github.com/square/kotlinpoet/issues/1557 secondaryConstructorForbiddenInAnnotationnull4179 @Test fun secondaryConstructorForbiddenInAnnotation() { 4180 val type = TypeSpec.annotationBuilder("Taco") 4181 4182 assertThrows<IllegalArgumentException> { 4183 type.primaryConstructor(FunSpec.constructorBuilder().build()) 4184 .addFunction( 4185 FunSpec.constructorBuilder() 4186 .addParameter("value", String::class) 4187 .build(), 4188 ).build() 4189 }.hasMessageThat().isEqualTo("annotation class Taco cannot declare member function constructor()") 4190 } 4191 4192 // https://github.com/square/kotlinpoet/issues/1556 abstractFunForbiddenInObjectnull4193 @Test fun abstractFunForbiddenInObject() { 4194 val type = TypeSpec.objectBuilder("Taco") 4195 4196 assertThrows<IllegalArgumentException> { 4197 type.addFunction( 4198 FunSpec.builder("eat") 4199 .addModifiers(ABSTRACT) 4200 .build(), 4201 ) 4202 .build() 4203 }.hasMessageThat().isEqualTo("non-abstract type Taco cannot declare abstract function eat") 4204 } 4205 classHeaderFormattingnull4206 @Test fun classHeaderFormatting() { 4207 val typeSpec = TypeSpec.classBuilder("Person") 4208 .addModifiers(DATA) 4209 .primaryConstructor( 4210 FunSpec.constructorBuilder() 4211 .addParameter("id", Int::class) 4212 .addParameter("name", String::class) 4213 .addParameter("surname", String::class) 4214 .build(), 4215 ) 4216 .addProperty( 4217 PropertySpec.builder("id", Int::class, KModifier.OVERRIDE) 4218 .initializer("id") 4219 .build(), 4220 ) 4221 .addProperty( 4222 PropertySpec.builder("name", String::class, KModifier.OVERRIDE) 4223 .initializer("name") 4224 .build(), 4225 ) 4226 .addProperty( 4227 PropertySpec.builder("surname", String::class, KModifier.OVERRIDE) 4228 .initializer("surname") 4229 .build(), 4230 ) 4231 .build() 4232 4233 assertThat(toString(typeSpec)).isEqualTo( 4234 """ 4235 |package com.squareup.tacos 4236 | 4237 |import kotlin.Int 4238 |import kotlin.String 4239 | 4240 |public data class Person( 4241 | override val id: Int, 4242 | override val name: String, 4243 | override val surname: String, 4244 |) 4245 | 4246 """.trimMargin(), 4247 ) 4248 } 4249 4250 @Test classHeaderAnnotationsnull4251 fun classHeaderAnnotations() { 4252 val idParameterSpec = ParameterSpec.builder("id", Int::class) 4253 .addAnnotation(ClassName("com.squareup.kotlinpoet", "Id")) 4254 .defaultValue("1") 4255 .build() 4256 4257 val typeSpec = TypeSpec.classBuilder("Person") 4258 .addModifiers(DATA) 4259 .primaryConstructor( 4260 FunSpec.constructorBuilder() 4261 .addParameter(idParameterSpec) 4262 .build(), 4263 ) 4264 .addProperty( 4265 PropertySpec.builder("id", Int::class) 4266 .addModifiers(PRIVATE) 4267 .initializer("id") 4268 .addAnnotation(ClassName("com.squareup.kotlinpoet", "OrderBy")) 4269 .build(), 4270 ) 4271 .build() 4272 4273 assertThat(toString(typeSpec)).isEqualTo( 4274 """ 4275 |package com.squareup.tacos 4276 | 4277 |import com.squareup.kotlinpoet.Id 4278 |import com.squareup.kotlinpoet.OrderBy 4279 |import kotlin.Int 4280 | 4281 |public data class Person( 4282 | @OrderBy 4283 | @Id 4284 | private val id: Int = 1, 4285 |) 4286 | 4287 """.trimMargin(), 4288 ) 4289 } 4290 literalPropertySpecnull4291 @Test fun literalPropertySpec() { 4292 val taco = TypeSpec.classBuilder("Taco") 4293 .addFunction( 4294 FunSpec.builder("shell") 4295 .addCode( 4296 CodeBlock.of( 4297 "%L", 4298 PropertySpec.builder("taco1", String::class.asTypeName()) 4299 .initializer("%S", "Taco!").build(), 4300 ), 4301 ) 4302 .addCode( 4303 CodeBlock.of( 4304 "%L", 4305 PropertySpec.builder("taco2", String::class.asTypeName().copy(nullable = true)) 4306 .initializer("null") 4307 .build(), 4308 ), 4309 ) 4310 .addCode( 4311 CodeBlock.of( 4312 "%L", 4313 PropertySpec.builder("taco3", String::class.asTypeName(), KModifier.LATEINIT) 4314 .mutable() 4315 .build(), 4316 ), 4317 ) 4318 .build(), 4319 ) 4320 .build() 4321 assertThat(toString(taco)).isEqualTo( 4322 """ 4323 |package com.squareup.tacos 4324 | 4325 |import kotlin.String 4326 | 4327 |public class Taco { 4328 | public fun shell() { 4329 | val taco1: String = "Taco!" 4330 | val taco2: String? = null 4331 | lateinit var taco3: String 4332 | } 4333 |} 4334 | 4335 """.trimMargin(), 4336 ) 4337 } 4338 basicDelegateTestnull4339 @Test fun basicDelegateTest() { 4340 val type = TypeSpec.classBuilder("Guac") 4341 .primaryConstructor( 4342 FunSpec.constructorBuilder() 4343 .addParameter("somethingElse", String::class) 4344 .build(), 4345 ) 4346 .addSuperinterface( 4347 Consumer::class.parameterizedBy(String::class), 4348 CodeBlock.of("({ println(it) })"), 4349 ) 4350 .build() 4351 4352 val expect = """ 4353 |package com.squareup.tacos 4354 | 4355 |import java.util.function.Consumer 4356 |import kotlin.String 4357 | 4358 |public class Guac( 4359 | somethingElse: String, 4360 |) : Consumer<String> by ({ println(it) }) 4361 | 4362 """.trimMargin() 4363 4364 assertThat(toString(type)).isEqualTo(expect) 4365 } 4366 testDelegateOnObjectnull4367 @Test fun testDelegateOnObject() { 4368 val type = TypeSpec.objectBuilder("Guac") 4369 .addSuperinterface( 4370 Consumer::class.parameterizedBy(String::class), 4371 CodeBlock.of("({ println(it) })"), 4372 ) 4373 .build() 4374 4375 val expect = """ 4376 |package com.squareup.tacos 4377 | 4378 |import java.util.function.Consumer 4379 |import kotlin.String 4380 | 4381 |public object Guac : Consumer<String> by ({ println(it) }) 4382 | 4383 """.trimMargin() 4384 4385 assertThat(toString(type)).isEqualTo(expect) 4386 } 4387 testMultipleDelegatesnull4388 @Test fun testMultipleDelegates() { 4389 val type = TypeSpec.classBuilder("StringToInteger") 4390 .primaryConstructor( 4391 FunSpec.constructorBuilder() 4392 .build(), 4393 ) 4394 .addSuperinterface( 4395 Function::class.parameterizedBy(String::class, Int::class), 4396 CodeBlock.of("Function ({ text -> text.toIntOrNull() ?: 0 })"), 4397 ) 4398 .addSuperinterface( 4399 Runnable::class, 4400 CodeBlock.of("Runnable ({ %T.debug(\"Hello world\") })", Logger::class.asTypeName()), 4401 ) 4402 .build() 4403 4404 val expect = """ 4405 |package com.squareup.tacos 4406 | 4407 |import java.lang.Runnable 4408 |import java.util.logging.Logger 4409 |import kotlin.Function 4410 |import kotlin.Int 4411 |import kotlin.String 4412 | 4413 |public class StringToInteger() : Function<String, Int> by Function ({ text -> text.toIntOrNull() ?: 4414 | 0 }), Runnable by Runnable ({ Logger.debug("Hello world") }) 4415 | 4416 """.trimMargin() 4417 4418 assertThat(toString(type)).isEqualTo(expect) 4419 } 4420 testNoSuchParameterDelegatenull4421 @Test fun testNoSuchParameterDelegate() { 4422 assertThrows<IllegalArgumentException> { 4423 TypeSpec.classBuilder("Taco") 4424 .primaryConstructor( 4425 FunSpec.constructorBuilder() 4426 .addParameter("other", String::class) 4427 .build(), 4428 ) 4429 .addSuperinterface(KFunction::class, "notOther") 4430 .build() 4431 }.hasMessageThat().isEqualTo("no such constructor parameter 'notOther' to delegate to for type 'Taco'") 4432 } 4433 failAddParamDelegateWhenNullConstructornull4434 @Test fun failAddParamDelegateWhenNullConstructor() { 4435 assertThrows<IllegalArgumentException> { 4436 TypeSpec.classBuilder("Taco") 4437 .addSuperinterface(Runnable::class, "etc") 4438 .build() 4439 }.hasMessageThat().isEqualTo("delegating to constructor parameter requires not-null constructor") 4440 } 4441 testAddedDelegateByParamNamenull4442 @Test fun testAddedDelegateByParamName() { 4443 val type = TypeSpec.classBuilder("Taco") 4444 .primaryConstructor( 4445 FunSpec.constructorBuilder() 4446 .addParameter("superString", Function::class) 4447 .build(), 4448 ) 4449 .addSuperinterface(Function::class, "superString") 4450 .build() 4451 4452 assertThat(toString(type)).isEqualTo( 4453 """ 4454 |package com.squareup.tacos 4455 | 4456 |import kotlin.Function 4457 | 4458 |public class Taco( 4459 | superString: Function, 4460 |) : Function by superString 4461 | 4462 """.trimMargin(), 4463 ) 4464 } 4465 failOnAddExistingDelegateTypenull4466 @Test fun failOnAddExistingDelegateType() { 4467 assertThrows<IllegalArgumentException> { 4468 TypeSpec.classBuilder("Taco") 4469 .primaryConstructor( 4470 FunSpec.constructorBuilder() 4471 .addParameter("superString", Function::class) 4472 .build(), 4473 ) 4474 .addSuperinterface(Function::class, CodeBlock.of("{ print(Hello) }")) 4475 .addSuperinterface(Function::class, "superString") 4476 .build() 4477 fail() 4478 }.hasMessageThat().isEqualTo( 4479 "'Taco' can not delegate to kotlin.Function " + 4480 "by superString with existing declaration by { print(Hello) }", 4481 ) 4482 } 4483 testDelegateIfaceWithOtherParamTypeNamenull4484 @Test fun testDelegateIfaceWithOtherParamTypeName() { 4485 val entity = ClassName(tacosPackage, "Entity") 4486 val entityBuilder = ClassName(tacosPackage, "EntityBuilder") 4487 val type = TypeSpec.classBuilder("EntityBuilder") 4488 .primaryConstructor( 4489 FunSpec.constructorBuilder() 4490 .addParameter( 4491 ParameterSpec.builder( 4492 "argBuilder", 4493 ClassName(tacosPackage, "Payload") 4494 .parameterizedBy(entityBuilder, entity), 4495 ) 4496 .defaultValue("Payload.create()") 4497 .build(), 4498 ) 4499 .build(), 4500 ) 4501 .addSuperinterface( 4502 ClassName(tacosPackage, "TypeBuilder") 4503 .parameterizedBy(entityBuilder, entity), 4504 "argBuilder", 4505 ) 4506 .build() 4507 4508 assertThat(toString(type)).isEqualTo( 4509 """ 4510 |package com.squareup.tacos 4511 | 4512 |public class EntityBuilder( 4513 | argBuilder: Payload<EntityBuilder, Entity> = Payload.create(), 4514 |) : TypeBuilder<EntityBuilder, Entity> by argBuilder 4515 | 4516 """.trimMargin(), 4517 ) 4518 } 4519 externalClassFunctionHasNoBodynull4520 @Test fun externalClassFunctionHasNoBody() { 4521 val typeSpec = TypeSpec.classBuilder("Foo") 4522 .addModifiers(KModifier.EXTERNAL) 4523 .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build()) 4524 .build() 4525 4526 assertThat(toString(typeSpec)).isEqualTo( 4527 """ 4528 |package com.squareup.tacos 4529 | 4530 |public external class Foo { 4531 | public fun bar() 4532 |} 4533 | 4534 """.trimMargin(), 4535 ) 4536 } 4537 externalInterfaceWithMembersnull4538 @Test fun externalInterfaceWithMembers() { 4539 val typeSpec = TypeSpec.interfaceBuilder("Foo") 4540 .addModifiers(KModifier.EXTERNAL) 4541 .addProperty(PropertySpec.builder("baz", String::class).addModifiers(KModifier.EXTERNAL).build()) 4542 .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build()) 4543 .build() 4544 4545 assertThat(toString(typeSpec)).isEqualTo( 4546 """ 4547 |package com.squareup.tacos 4548 | 4549 |import kotlin.String 4550 | 4551 |public external interface Foo { 4552 | public val baz: String 4553 | 4554 | public fun bar() 4555 |} 4556 | 4557 """.trimMargin(), 4558 ) 4559 } 4560 externalObjectWithMembersnull4561 @Test fun externalObjectWithMembers() { 4562 val typeSpec = TypeSpec.objectBuilder("Foo") 4563 .addModifiers(KModifier.EXTERNAL) 4564 .addProperty(PropertySpec.builder("baz", String::class).addModifiers(KModifier.EXTERNAL).build()) 4565 .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build()) 4566 .build() 4567 4568 assertThat(toString(typeSpec)).isEqualTo( 4569 """ 4570 |package com.squareup.tacos 4571 | 4572 |import kotlin.String 4573 | 4574 |public external object Foo { 4575 | public val baz: String 4576 | 4577 | public fun bar() 4578 |} 4579 | 4580 """.trimMargin(), 4581 ) 4582 } 4583 externalClassWithNestedTypesnull4584 @Test fun externalClassWithNestedTypes() { 4585 val typeSpec = TypeSpec.classBuilder("Foo") 4586 .addModifiers(KModifier.EXTERNAL) 4587 .addType( 4588 TypeSpec.classBuilder("Nested1") 4589 .addModifiers(KModifier.EXTERNAL) 4590 .addType( 4591 TypeSpec.objectBuilder("Nested2") 4592 .addModifiers(KModifier.EXTERNAL) 4593 .addFunction(FunSpec.builder("bar").addModifiers(KModifier.EXTERNAL).build()) 4594 .build(), 4595 ) 4596 .addFunction(FunSpec.builder("baz").addModifiers(KModifier.EXTERNAL).build()) 4597 .build(), 4598 ) 4599 .addType( 4600 TypeSpec.companionObjectBuilder() 4601 .addModifiers(KModifier.EXTERNAL) 4602 .addFunction(FunSpec.builder("qux").addModifiers(KModifier.EXTERNAL).build()) 4603 .build(), 4604 ) 4605 .build() 4606 4607 assertThat(toString(typeSpec)).isEqualTo( 4608 """ 4609 |package com.squareup.tacos 4610 | 4611 |public external class Foo { 4612 | public class Nested1 { 4613 | public fun baz() 4614 | 4615 | public object Nested2 { 4616 | public fun bar() 4617 | } 4618 | } 4619 | 4620 | public companion object { 4621 | public fun qux() 4622 | } 4623 |} 4624 | 4625 """.trimMargin(), 4626 ) 4627 } 4628 isEnumnull4629 @Test fun isEnum() { 4630 val enum = TypeSpec.enumBuilder("Topping") 4631 .addEnumConstant("CHEESE") 4632 .build() 4633 assertThat(enum.isEnum).isTrue() 4634 } 4635 isAnnotationnull4636 @Test fun isAnnotation() { 4637 val annotation = TypeSpec.annotationBuilder("Taco") 4638 .build() 4639 assertThat(annotation.isAnnotation).isTrue() 4640 } 4641 escapePunctuationInTypeNamenull4642 @Test fun escapePunctuationInTypeName() { 4643 assertThat(TypeSpec.classBuilder("With-Hyphen").build().toString()).isEqualTo( 4644 """ 4645 |public class `With-Hyphen` 4646 | 4647 """.trimMargin(), 4648 ) 4649 } 4650 multipleCompanionObjectsnull4651 @Test fun multipleCompanionObjects() { 4652 assertThrows<IllegalArgumentException> { 4653 TypeSpec.classBuilder("Taco") 4654 .addTypes( 4655 listOf( 4656 TypeSpec.companionObjectBuilder() 4657 .build(), 4658 TypeSpec.companionObjectBuilder() 4659 .build(), 4660 ), 4661 ) 4662 .build() 4663 } 4664 } 4665 objectKindIsCompanionnull4666 @Test fun objectKindIsCompanion() { 4667 val companionObject = TypeSpec.companionObjectBuilder() 4668 .build() 4669 assertThat(companionObject.isCompanion).isTrue() 4670 } 4671 typeNamesCollisionnull4672 @Test fun typeNamesCollision() { 4673 val sqlTaco = ClassName("java.sql", "Taco") 4674 val source = FileSpec.builder("com.squareup.tacos", "Taco") 4675 .addType( 4676 TypeSpec.classBuilder("Taco") 4677 .addModifiers(DATA) 4678 .addProperty( 4679 PropertySpec.builder("madeFreshDatabaseDate", sqlTaco) 4680 .initializer("madeFreshDatabaseDate") 4681 .build(), 4682 ) 4683 .primaryConstructor( 4684 FunSpec.constructorBuilder() 4685 .addParameter("madeFreshDatabaseDate", sqlTaco) 4686 .addParameter("fooNt", INT) 4687 .build(), 4688 ) 4689 .addFunction( 4690 FunSpec.constructorBuilder() 4691 .addParameter("anotherTaco", ClassName("com.squareup.tacos", "Taco")) 4692 .callThisConstructor(CodeBlock.of("%T.defaultInstance(), 0", sqlTaco)) 4693 .build(), 4694 ) 4695 .build(), 4696 ) 4697 .build() 4698 assertThat(source.toString()).isEqualTo( 4699 """ 4700 |package com.squareup.tacos 4701 | 4702 |import kotlin.Int 4703 | 4704 |public data class Taco( 4705 | public val madeFreshDatabaseDate: java.sql.Taco, 4706 | fooNt: Int, 4707 |) { 4708 | public constructor(anotherTaco: Taco) : this(java.sql.Taco.defaultInstance(), 0) 4709 |} 4710 | 4711 """.trimMargin(), 4712 ) 4713 } 4714 modifyAnnotationsnull4715 @Test fun modifyAnnotations() { 4716 val builder = TypeSpec.classBuilder("Taco") 4717 .addAnnotation( 4718 AnnotationSpec.builder(JvmName::class.asClassName()) 4719 .addMember("name = %S", "jvmWord") 4720 .build(), 4721 ) 4722 4723 val javaWord = AnnotationSpec.builder(JvmName::class.asClassName()) 4724 .addMember("name = %S", "javaWord") 4725 .build() 4726 builder.annotations.clear() 4727 builder.annotations.add(javaWord) 4728 4729 assertThat(builder.build().annotations).containsExactly(javaWord) 4730 } 4731 modifyTypeVariableNamesnull4732 @Test fun modifyTypeVariableNames() { 4733 val builder = TypeSpec.classBuilder("Taco") 4734 .addTypeVariable(TypeVariableName("V")) 4735 4736 val tVar = TypeVariableName("T") 4737 builder.typeVariables.clear() 4738 builder.typeVariables.add(tVar) 4739 4740 assertThat(builder.build().typeVariables).containsExactly(tVar) 4741 } 4742 modifyFunctionsnull4743 @Test fun modifyFunctions() { 4744 val builder = TypeSpec.classBuilder("Taco") 4745 .addFunction(FunSpec.builder("topping").build()) 4746 4747 val seasoning = FunSpec.builder("seasoning").build() 4748 builder.funSpecs.clear() 4749 builder.funSpecs.add(seasoning) 4750 4751 assertThat(builder.build().funSpecs).containsExactly(seasoning) 4752 } 4753 modifyTypeSpecsnull4754 @Test fun modifyTypeSpecs() { 4755 val builder = TypeSpec.classBuilder("Taco") 4756 .addType(TypeSpec.classBuilder("Topping").build()) 4757 4758 val seasoning = TypeSpec.classBuilder("Seasoning").build() 4759 builder.typeSpecs.clear() 4760 builder.typeSpecs.add(seasoning) 4761 4762 assertThat(builder.build().typeSpecs).containsExactly(seasoning) 4763 } 4764 modifySuperinterfacesnull4765 @Test fun modifySuperinterfaces() { 4766 val builder = TypeSpec.classBuilder("Taco") 4767 .addSuperinterface(List::class) 4768 4769 builder.superinterfaces.clear() 4770 builder.superinterfaces[Set::class.asTypeName()] = CodeBlock.EMPTY 4771 4772 assertThat(builder.build().superinterfaces) 4773 .containsExactlyEntriesIn(mapOf(Set::class.asTypeName() to CodeBlock.EMPTY)) 4774 } 4775 modifyPropertiesnull4776 @Test fun modifyProperties() { 4777 val builder = TypeSpec.classBuilder("Taco") 4778 .addProperty(PropertySpec.builder("topping", String::class.asClassName()).build()) 4779 4780 val seasoning = PropertySpec.builder("seasoning", String::class.asClassName()).build() 4781 builder.propertySpecs.clear() 4782 builder.propertySpecs.add(seasoning) 4783 4784 assertThat(builder.build().propertySpecs).containsExactly(seasoning) 4785 } 4786 modifyEnumConstantsnull4787 @Test fun modifyEnumConstants() { 4788 val builder = TypeSpec.enumBuilder("Taco") 4789 .addEnumConstant("TOPPING") 4790 4791 builder.enumConstants.clear() 4792 builder.enumConstants["SEASONING"] = TypeSpec.anonymousClassBuilder().build() 4793 4794 assertThat(builder.build().enumConstants) 4795 .containsExactlyEntriesIn(mapOf("SEASONING" to TypeSpec.anonymousClassBuilder().build())) 4796 } 4797 modifySuperclassConstructorParamsnull4798 @Test fun modifySuperclassConstructorParams() { 4799 val builder = TypeSpec.classBuilder("Taco") 4800 .addSuperclassConstructorParameter(CodeBlock.of("seasoning = %S", "mild")) 4801 4802 val seasoning = CodeBlock.of("seasoning = %S", "spicy") 4803 builder.superclassConstructorParameters.clear() 4804 builder.superclassConstructorParameters.add(seasoning) 4805 4806 assertThat(builder.build().superclassConstructorParameters).containsExactly(seasoning) 4807 } 4808 4809 // https://github.com/square/kotlinpoet/issues/565 markerEnumnull4810 @Test fun markerEnum() { 4811 val spec = TypeSpec.enumBuilder("Topping") 4812 .build() 4813 assertThat(spec.toString()).isEqualTo( 4814 """ 4815 |public enum class Topping 4816 | 4817 """.trimMargin(), 4818 ) 4819 } 4820 4821 // https://github.com/square/kotlinpoet/issues/586 classKdocWithoutTagsnull4822 @Test fun classKdocWithoutTags() { 4823 val typeSpec = TypeSpec.classBuilder("Foo") 4824 .addKdoc("blah blah") 4825 .build() 4826 assertThat(typeSpec.toString()).isEqualTo( 4827 """ 4828 |/** 4829 | * blah blah 4830 | */ 4831 |public class Foo 4832 | 4833 """.trimMargin(), 4834 ) 4835 } 4836 classWithPropertyKdocnull4837 @Test fun classWithPropertyKdoc() { 4838 val typeSpec = TypeSpec.classBuilder("Foo") 4839 .addProperty( 4840 PropertySpec.builder("bar", String::class) 4841 .addKdoc("The bar for your foo") 4842 .build(), 4843 ) 4844 .build() 4845 assertThat(typeSpec.toString()).isEqualTo( 4846 """ 4847 |public class Foo { 4848 | /** 4849 | * The bar for your foo 4850 | */ 4851 | public val bar: kotlin.String 4852 |} 4853 | 4854 """.trimMargin(), 4855 ) 4856 } 4857 4858 // https://github.com/square/kotlinpoet/issues/563 kdocFormattingnull4859 @Test fun kdocFormatting() { 4860 val typeSpec = TypeSpec.classBuilder("MyType") 4861 .addKdoc("This is a thing for stuff.") 4862 .addProperty( 4863 PropertySpec.builder("first", INT) 4864 .initializer("first") 4865 .build(), 4866 ) 4867 .addProperty( 4868 PropertySpec.builder("second", INT) 4869 .initializer("second") 4870 .build(), 4871 ) 4872 .addProperty( 4873 PropertySpec.builder("third", INT) 4874 .initializer("third") 4875 .build(), 4876 ) 4877 .primaryConstructor( 4878 FunSpec.constructorBuilder() 4879 .addKdoc("Construct a thing!") 4880 .addParameter( 4881 ParameterSpec.builder("first", INT) 4882 .addKdoc("the first thing") 4883 .build(), 4884 ) 4885 .addParameter( 4886 ParameterSpec.builder("second", INT) 4887 .addKdoc("the second thing") 4888 .build(), 4889 ) 4890 .addParameter( 4891 ParameterSpec.builder("third", INT) 4892 .addKdoc("the third thing") 4893 .build(), 4894 ) 4895 .build(), 4896 ) 4897 .build() 4898 assertThat(typeSpec.toString()).isEqualTo( 4899 """ 4900 |/** 4901 | * This is a thing for stuff. 4902 | * 4903 | * @constructor Construct a thing! 4904 | * @param first the first thing 4905 | * @param second the second thing 4906 | * @param third the third thing 4907 | */ 4908 |public class MyType( 4909 | /** 4910 | * the first thing 4911 | */ 4912 | public val first: kotlin.Int, 4913 | /** 4914 | * the second thing 4915 | */ 4916 | public val second: kotlin.Int, 4917 | /** 4918 | * the third thing 4919 | */ 4920 | public val third: kotlin.Int, 4921 |) 4922 | 4923 """.trimMargin(), 4924 ) 4925 } 4926 primaryConstructorWithOneParameterKdocFormattingnull4927 @Test fun primaryConstructorWithOneParameterKdocFormatting() { 4928 val typeSpec = TypeSpec.classBuilder("MyType") 4929 .primaryConstructor( 4930 FunSpec.constructorBuilder() 4931 .addParameter( 4932 ParameterSpec.builder("first", INT) 4933 .addKdoc("the first thing") 4934 .build(), 4935 ) 4936 .build(), 4937 ) 4938 .build() 4939 assertThat(typeSpec.toString()).isEqualTo( 4940 """ 4941 |/** 4942 | * @param first the first thing 4943 | */ 4944 |public class MyType( 4945 | first: kotlin.Int, 4946 |) 4947 | 4948 """.trimMargin(), 4949 ) 4950 } 4951 4952 // https://github.com/square/kotlinpoet/issues/594 longCommentnull4953 @Test fun longComment() { 4954 val taco = TypeSpec.classBuilder("Taco") 4955 .addFunction( 4956 FunSpec.builder("getAnswer") 4957 .returns(Int::class) 4958 .addComment( 4959 "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do " + 4960 "eiusmod tempor incididunt ut labore et dolore magna aliqua.", 4961 ) 4962 .addStatement("return 42") 4963 .build(), 4964 ) 4965 .build() 4966 assertThat(toString(taco)).isEqualTo( 4967 """ 4968 |package com.squareup.tacos 4969 | 4970 |import kotlin.Int 4971 | 4972 |public class Taco { 4973 | public fun getAnswer(): Int { 4974 | // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 4975 | return 42 4976 | } 4977 |} 4978 | 4979 """.trimMargin(), 4980 ) 4981 } 4982 originatingElementsIncludesThoseOfNestedTypesnull4983 @Test fun originatingElementsIncludesThoseOfNestedTypes() { 4984 val outerElement = FakeElement() 4985 val innerElement = FakeElement() 4986 val outer = TypeSpec.classBuilder("Outer") 4987 .addOriginatingElement(outerElement) 4988 .addType( 4989 TypeSpec.classBuilder("Inner") 4990 .addOriginatingElement(innerElement) 4991 .build(), 4992 ) 4993 .build() 4994 assertThat(outer.originatingElements).containsExactly(outerElement, innerElement) 4995 } 4996 4997 // https://github.com/square/kotlinpoet/issues/698 escapeEnumConstantsnull4998 @Test fun escapeEnumConstants() { 4999 val enum = TypeSpec.enumBuilder("MyEnum") 5000 .addEnumConstant("test test") 5001 .addEnumConstant("0constants") 5002 .build() 5003 assertThat(enum.toString()).isEqualTo( 5004 """ 5005 |public enum class MyEnum { 5006 | `test test`, 5007 | `0constants`, 5008 |} 5009 | 5010 """.trimMargin(), 5011 ) 5012 } 5013 initOrdering_firstnull5014 @Test fun initOrdering_first() { 5015 val type = TypeSpec.classBuilder("MyClass") 5016 .addInitializerBlock(CodeBlock.builder().build()) 5017 .addProperty("tacos", Int::class) 5018 .build() 5019 5020 //language=kotlin 5021 assertThat(toString(type)).isEqualTo( 5022 """ 5023 package com.squareup.tacos 5024 5025 import kotlin.Int 5026 5027 public class MyClass { 5028 init { 5029 } 5030 5031 public val tacos: Int 5032 } 5033 5034 """.trimIndent(), 5035 ) 5036 } 5037 initOrdering_middlenull5038 @Test fun initOrdering_middle() { 5039 val type = TypeSpec.classBuilder("MyClass") 5040 .addProperty("tacos1", Int::class) 5041 .addInitializerBlock(CodeBlock.builder().build()) 5042 .addProperty("tacos2", Int::class) 5043 .build() 5044 5045 //language=kotlin 5046 assertThat(toString(type)).isEqualTo( 5047 """ 5048 package com.squareup.tacos 5049 5050 import kotlin.Int 5051 5052 public class MyClass { 5053 public val tacos1: Int 5054 5055 init { 5056 } 5057 5058 public val tacos2: Int 5059 } 5060 5061 """.trimIndent(), 5062 ) 5063 } 5064 initOrdering_lastnull5065 @Test fun initOrdering_last() { 5066 val type = TypeSpec.classBuilder("MyClass") 5067 .addProperty("tacos", Int::class) 5068 .addInitializerBlock(CodeBlock.builder().build()) 5069 .build() 5070 5071 //language=kotlin 5072 assertThat(toString(type)).isEqualTo( 5073 """ 5074 package com.squareup.tacos 5075 5076 import kotlin.Int 5077 5078 public class MyClass { 5079 public val tacos: Int 5080 5081 init { 5082 } 5083 } 5084 5085 """.trimIndent(), 5086 ) 5087 } 5088 initOrdering_constructorParamsExludedAfterIndexnull5089 @Test fun initOrdering_constructorParamsExludedAfterIndex() { 5090 val type = TypeSpec.classBuilder("MyClass") 5091 .primaryConstructor( 5092 FunSpec.constructorBuilder() 5093 .addParameter("tacos1", Int::class) 5094 .addParameter("tacos2", Int::class) 5095 .build(), 5096 ) 5097 .addProperty( 5098 PropertySpec.builder("tacos1", Int::class) 5099 .initializer("tacos1") 5100 .build(), 5101 ) 5102 .addInitializerBlock(CodeBlock.builder().build()) 5103 .addProperty( 5104 PropertySpec.builder("tacos2", Int::class) 5105 .initializer("tacos2") 5106 .build(), 5107 ) 5108 .build() 5109 5110 //language=kotlin 5111 assertThat(toString(type)).isEqualTo( 5112 """ 5113 package com.squareup.tacos 5114 5115 import kotlin.Int 5116 5117 public class MyClass( 5118 public val tacos1: Int, 5119 tacos2: Int, 5120 ) { 5121 init { 5122 } 5123 5124 public val tacos2: Int = tacos2 5125 } 5126 5127 """.trimIndent(), 5128 ) 5129 } 5130 5131 // https://github.com/square/kotlinpoet/issues/843 kdocWithParametersWithoutClassKdocnull5132 @Test fun kdocWithParametersWithoutClassKdoc() { 5133 val taco = TypeSpec.classBuilder("Taco") 5134 .primaryConstructor( 5135 FunSpec.constructorBuilder() 5136 .addParameter( 5137 ParameterSpec.builder("mild", Boolean::class) 5138 .addKdoc(CodeBlock.of("%L", "Whether the taco is mild (ew) or crunchy (ye).\n")) 5139 .build(), 5140 ) 5141 .build(), 5142 ) 5143 .addProperty( 5144 PropertySpec.builder("mild", Boolean::class) 5145 .addKdoc("No one likes mild tacos.") 5146 .initializer("mild") 5147 .build(), 5148 ) 5149 .build() 5150 assertThat(toString(taco)).isEqualTo( 5151 """ 5152 |package com.squareup.tacos 5153 | 5154 |import kotlin.Boolean 5155 | 5156 |/** 5157 | * @param mild Whether the taco is mild (ew) or crunchy (ye). 5158 | */ 5159 |public class Taco( 5160 | /** 5161 | * No one likes mild tacos. 5162 | */ 5163 | public val mild: Boolean, 5164 |) 5165 | 5166 """.trimMargin(), 5167 ) 5168 } 5169 5170 // https://github.com/square/kotlinpoet/issues/848 escapeEnumConstantNamesnull5171 @Test fun escapeEnumConstantNames() { 5172 val enum = TypeSpec 5173 .enumBuilder("MyEnum") 5174 .addEnumConstant("object") 5175 .build() 5176 assertThat(toString(enum)).isEqualTo( 5177 """ 5178 |package com.squareup.tacos 5179 | 5180 |public enum class MyEnum { 5181 | `object`, 5182 |} 5183 | 5184 """.trimMargin(), 5185 ) 5186 } 5187 5188 // https://youtrack.jetbrains.com/issue/KT-52315 escapeHeaderAndImplAsEnumConstantNamesnull5189 @Test fun escapeHeaderAndImplAsEnumConstantNames() { 5190 val primaryConstructor = FunSpec.constructorBuilder() 5191 .addParameter("int", Int::class) 5192 .build() 5193 val enum = TypeSpec 5194 .enumBuilder("MyEnum") 5195 .primaryConstructor(primaryConstructor) 5196 .addEnumConstant( 5197 "header", 5198 TypeSpec.anonymousClassBuilder() 5199 .addSuperclassConstructorParameter("%L", 1) 5200 .build(), 5201 ) 5202 .addEnumConstant( 5203 "impl", 5204 TypeSpec.anonymousClassBuilder() 5205 .addSuperclassConstructorParameter("%L", 2) 5206 .build(), 5207 ) 5208 .build() 5209 assertThat(toString(enum)).isEqualTo( 5210 """ 5211 |package com.squareup.tacos 5212 | 5213 |import kotlin.Int 5214 | 5215 |public enum class MyEnum( 5216 | int: Int, 5217 |) { 5218 | `header`(1), 5219 | `impl`(2), 5220 |} 5221 | 5222 """.trimMargin(), 5223 ) 5224 } 5225 escapeClassNamesnull5226 @Test fun escapeClassNames() { 5227 val type = TypeSpec.classBuilder("fun").build() 5228 assertThat(type.toString()).isEqualTo( 5229 """ 5230 |public class `fun` 5231 | 5232 """.trimMargin(), 5233 ) 5234 } 5235 escapeInnerClassNamenull5236 @Test fun escapeInnerClassName() { 5237 val tacoType = ClassName("com.squareup.tacos", "Taco", "object") 5238 val funSpec = FunSpec.builder("printTaco") 5239 .addParameter("taco", tacoType) 5240 .addStatement("print(taco)") 5241 .build() 5242 assertThat(funSpec.toString()).isEqualTo( 5243 """ 5244 |public fun printTaco(taco: com.squareup.tacos.Taco.`object`) { 5245 | print(taco) 5246 |} 5247 | 5248 """.trimMargin(), 5249 ) 5250 } 5251 escapeAllowedCharactersnull5252 @Test fun escapeAllowedCharacters() { 5253 val typeSpec = TypeSpec.classBuilder("A\$B") 5254 .build() 5255 assertThat(typeSpec.toString()).isEqualTo("public class `A\$B`\n") 5256 } 5257 5258 // https://github.com/square/kotlinpoet/issues/1011 abstractInterfaceMembersnull5259 @Test fun abstractInterfaceMembers() { 5260 val file = FileSpec.builder("com.squareup.tacos", "Taco") 5261 .addType( 5262 TypeSpec.interfaceBuilder("Taco") 5263 .addProperty("foo", String::class, ABSTRACT) 5264 .addProperty( 5265 PropertySpec.builder("fooWithDefault", String::class) 5266 .initializer("%S", "defaultValue") 5267 .build(), 5268 ) 5269 .addFunction( 5270 FunSpec.builder("bar") 5271 .addModifiers(ABSTRACT) 5272 .returns(String::class) 5273 .build(), 5274 ) 5275 .addFunction( 5276 FunSpec.builder("barWithDefault") 5277 .build(), 5278 ) 5279 .build(), 5280 ) 5281 .build() 5282 // language=kotlin 5283 assertThat(file.toString()).isEqualTo( 5284 """ 5285 package com.squareup.tacos 5286 5287 import kotlin.String 5288 5289 public interface Taco { 5290 public val foo: String 5291 5292 public val fooWithDefault: String = "defaultValue" 5293 5294 public fun bar(): String 5295 5296 public fun barWithDefault() { 5297 } 5298 } 5299 5300 """.trimIndent(), 5301 ) 5302 } 5303 emptyConstructorGeneratednull5304 @Test fun emptyConstructorGenerated() { 5305 val taco = TypeSpec.classBuilder("Taco") 5306 .primaryConstructor(FunSpec.constructorBuilder().build()) 5307 .build() 5308 val file = FileSpec.builder("com.squareup.tacos", "Taco") 5309 .addType(taco) 5310 .build() 5311 assertThat(file.toString()).isEqualTo( 5312 """ 5313 package com.squareup.tacos 5314 5315 public class Taco() 5316 5317 """.trimIndent(), 5318 ) 5319 } 5320 5321 // Regression test for https://github.com/square/kotlinpoet/issues/1176 templates in class delegation blocks should be imported toonull5322 @Test fun `templates in class delegation blocks should be imported too`() { 5323 val taco = TypeSpec.classBuilder("TacoShim") 5324 .addSuperinterface( 5325 ClassName("test", "Taco"), 5326 CodeBlock.of("%T", ClassName("test", "RealTaco")), 5327 ) 5328 .build() 5329 val file = FileSpec.builder("com.squareup.tacos", "Tacos") 5330 .addType(taco) 5331 .build() 5332 assertThat(file.toString()).isEqualTo( 5333 """ 5334 package com.squareup.tacos 5335 5336 import test.RealTaco 5337 import test.Taco 5338 5339 public class TacoShim : Taco by RealTaco 5340 5341 """.trimIndent(), 5342 ) 5343 } 5344 5345 // https://github.com/square/kotlinpoet/issues/1183 forbidden enum constant namesnull5346 @Test fun `forbidden enum constant names`() { 5347 var exception = assertFailsWith<IllegalArgumentException> { 5348 TypeSpec.enumBuilder("Topping") 5349 .addEnumConstant("name") 5350 } 5351 assertThat(exception.message).isEqualTo( 5352 "constant with name \"name\" conflicts with a supertype member with the same name", 5353 ) 5354 5355 @Suppress("RemoveExplicitTypeArguments") 5356 exception = assertFailsWith<IllegalArgumentException> { 5357 TypeSpec.enumBuilder("Topping") 5358 .addEnumConstant("ordinal") 5359 } 5360 assertThat(exception.message).isEqualTo( 5361 "constant with name \"ordinal\" conflicts with a supertype member with the same name", 5362 ) 5363 } 5364 5365 // https://github.com/square/kotlinpoet/issues/1183 forbidden enum property namesnull5366 @Test fun `forbidden enum property names`() { 5367 var exception = assertFailsWith<IllegalArgumentException> { 5368 TypeSpec.enumBuilder("Topping") 5369 .addProperty("name", String::class) 5370 } 5371 assertThat(exception.message).isEqualTo( 5372 "name is a final supertype member and can't be redeclared or overridden", 5373 ) 5374 5375 @Suppress("RemoveExplicitTypeArguments") 5376 exception = assertFailsWith<IllegalArgumentException> { 5377 TypeSpec.enumBuilder("Topping") 5378 .addProperty("ordinal", String::class) 5379 } 5380 assertThat(exception.message).isEqualTo( 5381 "ordinal is a final supertype member and can't be redeclared or overridden", 5382 ) 5383 } 5384 5385 // https://github.com/square/kotlinpoet/issues/1234 enum constants are resolvednull5386 @Test fun `enum constants are resolved`() { 5387 val file = FileSpec.builder("com.example", "test") 5388 .addType( 5389 TypeSpec.enumBuilder("Foo") 5390 .addProperty( 5391 PropertySpec.builder("rawValue", String::class) 5392 .initializer("%S", "") 5393 .build(), 5394 ) 5395 .addEnumConstant("String") 5396 .build(), 5397 ) 5398 .build() 5399 5400 assertThat(file.toString()).isEqualTo( 5401 """ 5402 package com.example 5403 5404 public enum class Foo { 5405 String, 5406 ; 5407 5408 public val rawValue: kotlin.String = "" 5409 } 5410 5411 """.trimIndent(), 5412 ) 5413 } 5414 5415 // https://github.com/square/kotlinpoet/issues/1035 dataClassWithKeywordPropertynull5416 @Test fun dataClassWithKeywordProperty() { 5417 val parameter = ParameterSpec.builder("data", STRING).build() 5418 val typeSpec = TypeSpec.classBuilder("Example") 5419 .addModifiers(DATA) 5420 .primaryConstructor( 5421 FunSpec.constructorBuilder() 5422 .addParameter(parameter) 5423 .build(), 5424 ) 5425 .addProperty( 5426 PropertySpec.builder(parameter.name, STRING) 5427 .initializer("%N", parameter) 5428 .build(), 5429 ) 5430 .build() 5431 assertThat(typeSpec.toString()).isEqualTo( 5432 """ 5433 public data class Example( 5434 public val `data`: kotlin.String, 5435 ) 5436 5437 """.trimIndent(), 5438 ) 5439 } 5440 5441 // https://github.com/square/kotlinpoet/issues/1548 overrideInternalAbstractFunctionVisibilitynull5442 @Test fun overrideInternalAbstractFunctionVisibility() { 5443 val baseClass = TypeSpec.classBuilder("Base") 5444 .addModifiers(PUBLIC, ABSTRACT) 5445 .addFunction( 5446 FunSpec.builder("foo") 5447 .addModifiers(INTERNAL, ABSTRACT) 5448 .build(), 5449 ) 5450 .build() 5451 assertThat(baseClass.toString()).isEqualTo( 5452 """ 5453 |public abstract class Base { 5454 | internal abstract fun foo() 5455 |} 5456 | 5457 """.trimMargin(), 5458 ) 5459 val bassClassName = ClassName("", "Base") 5460 val exampleClass = TypeSpec.classBuilder("Example") 5461 .addModifiers(PUBLIC) 5462 .superclass(bassClassName) 5463 .addFunction( 5464 FunSpec.builder("foo") 5465 .addModifiers(KModifier.OVERRIDE) 5466 .build(), 5467 ) 5468 .build() 5469 assertThat(exampleClass.toString()).isEqualTo( 5470 """ 5471 |public class Example : Base() { 5472 | override fun foo() { 5473 | } 5474 |} 5475 | 5476 """.trimMargin(), 5477 ) 5478 val example2Class = TypeSpec.classBuilder("Example2") 5479 .addModifiers(PUBLIC) 5480 .superclass(bassClassName) 5481 .addFunction( 5482 FunSpec.builder("foo") 5483 .addModifiers(PUBLIC, KModifier.OVERRIDE) 5484 .build(), 5485 ) 5486 .build() 5487 // Don't omit the public modifier here, 5488 // as we're explicitly increasing the visibility of this method in the subclass. 5489 assertThat(example2Class.toString()).isEqualTo( 5490 """ 5491 |public class Example2 : Base() { 5492 | public override fun foo() { 5493 | } 5494 |} 5495 | 5496 """.trimMargin(), 5497 ) 5498 val example3Class = TypeSpec.classBuilder("Example3") 5499 .addModifiers(INTERNAL) 5500 .superclass(bassClassName) 5501 .addFunction( 5502 FunSpec.builder("foo") 5503 .addModifiers(PUBLIC, KModifier.OVERRIDE) 5504 .build(), 5505 ) 5506 .build() 5507 // Don't omit the public modifier here, 5508 // as we're explicitly increasing the visibility of this method in the subclass. 5509 assertThat(example3Class.toString()).isEqualTo( 5510 """ 5511 |internal class Example3 : Base() { 5512 | public override fun foo() { 5513 | } 5514 |} 5515 | 5516 """.trimMargin(), 5517 ) 5518 } 5519 contextReceivernull5520 @Test fun contextReceiver() { 5521 val typeSpec = TypeSpec.classBuilder("Example") 5522 .contextReceivers(STRING) 5523 .build() 5524 5525 assertThat(typeSpec.toString()).isEqualTo( 5526 """ 5527 |context(kotlin.String) 5528 |public class Example 5529 | 5530 """.trimMargin(), 5531 ) 5532 } 5533 contextReceiver_mustBeClassnull5534 @Test fun contextReceiver_mustBeClass() { 5535 val t = assertFailsWith<IllegalStateException> { 5536 TypeSpec.interfaceBuilder("Example") 5537 .contextReceivers(STRING) 5538 } 5539 assertThat(t).hasMessageThat().contains("contextReceivers can only be applied on simple classes") 5540 } 5541 valWithContextReceiverWithoutGetternull5542 @Test fun valWithContextReceiverWithoutGetter() { 5543 assertThrows<IllegalArgumentException> { 5544 TypeSpec.classBuilder("Example") 5545 .addProperty( 5546 PropertySpec.builder("foo", STRING) 5547 .mutable(false) 5548 .contextReceivers(INT) 5549 .build(), 5550 ) 5551 .build() 5552 }.hasMessageThat() 5553 .isEqualTo("non-abstract properties with context receivers require a get()") 5554 } 5555 varWithContextReceiverWithoutAccessorsnull5556 @Test fun varWithContextReceiverWithoutAccessors() { 5557 assertThrows<IllegalArgumentException> { 5558 TypeSpec.classBuilder("Example") 5559 .addProperty( 5560 PropertySpec.builder("foo", STRING) 5561 .mutable() 5562 .contextReceivers(INT) 5563 .getter( 5564 FunSpec.getterBuilder() 5565 .build(), 5566 ) 5567 .build(), 5568 ).build() 5569 }.hasMessageThat() 5570 .isEqualTo("non-abstract mutable properties with context receivers require a set()") 5571 5572 assertThrows<IllegalArgumentException> { 5573 TypeSpec.classBuilder("Example") 5574 .addProperty( 5575 PropertySpec.builder("foo", STRING) 5576 .mutable() 5577 .contextReceivers(INT) 5578 .setter( 5579 FunSpec.setterBuilder() 5580 .build(), 5581 ) 5582 .build(), 5583 ).build() 5584 }.hasMessageThat() 5585 .isEqualTo("non-abstract properties with context receivers require a get()") 5586 } 5587 5588 // https://github.com/square/kotlinpoet/issues/1525 propertyWithContextReceiverInInterfacenull5589 @Test fun propertyWithContextReceiverInInterface() { 5590 val typeSpec = TypeSpec.interfaceBuilder("Bar") 5591 .addProperty( 5592 PropertySpec.builder("foo", Int::class) 5593 .contextReceivers(STRING) 5594 .build(), 5595 ) 5596 .addProperty( 5597 PropertySpec.builder("bar", Int::class) 5598 .contextReceivers(STRING) 5599 .mutable(true) 5600 .build(), 5601 ) 5602 .build() 5603 5604 assertThat(typeSpec.toString()).isEqualTo( 5605 """ 5606 |public interface Bar { 5607 | context(kotlin.String) 5608 | public val foo: kotlin.Int 5609 | 5610 | context(kotlin.String) 5611 | public var bar: kotlin.Int 5612 |} 5613 | 5614 """.trimMargin(), 5615 ) 5616 } 5617 nonAbstractPropertyWithContextReceiverInAbstractClassnull5618 @Test fun nonAbstractPropertyWithContextReceiverInAbstractClass() { 5619 assertThrows<IllegalArgumentException> { 5620 TypeSpec.classBuilder("Bar") 5621 .addModifiers(ABSTRACT) 5622 .addProperty( 5623 PropertySpec.builder("foo", Int::class) 5624 .contextReceivers(STRING) 5625 .build(), 5626 ) 5627 .build() 5628 }.hasMessageThat().isEqualTo("non-abstract properties with context receivers require a get()") 5629 } 5630 abstractPropertyWithContextReceiverInAbstractClassnull5631 @Test fun abstractPropertyWithContextReceiverInAbstractClass() { 5632 val typeSpec = TypeSpec.classBuilder("Bar") 5633 .addModifiers(ABSTRACT) 5634 .addProperty( 5635 PropertySpec.builder("foo", Int::class) 5636 .contextReceivers(STRING) 5637 .addModifiers(ABSTRACT) 5638 .build(), 5639 ) 5640 .build() 5641 5642 assertThat(typeSpec.toString()).isEqualTo( 5643 """ 5644 |public abstract class Bar { 5645 | context(kotlin.String) 5646 | public abstract val foo: kotlin.Int 5647 |} 5648 | 5649 """.trimMargin(), 5650 ) 5651 } 5652 abstractPropertyInNonAbstractClassnull5653 @Test fun abstractPropertyInNonAbstractClass() { 5654 assertThrows<IllegalArgumentException> { 5655 TypeSpec.classBuilder("Bar") 5656 .addProperty( 5657 PropertySpec.builder("foo", Int::class) 5658 .addModifiers(ABSTRACT) 5659 .build(), 5660 ) 5661 .build() 5662 }.hasMessageThat().isEqualTo("non-abstract type Bar cannot declare abstract property foo") 5663 } 5664 abstractPropertyInObjectnull5665 @Test fun abstractPropertyInObject() { 5666 assertThrows<IllegalArgumentException> { 5667 TypeSpec.objectBuilder("Bar") 5668 .addProperty( 5669 PropertySpec.builder("foo", Int::class) 5670 .addModifiers(ABSTRACT) 5671 .build(), 5672 ) 5673 .build() 5674 }.hasMessageThat().isEqualTo("non-abstract type Bar cannot declare abstract property foo") 5675 } 5676 abstractPropertyInEnumnull5677 @Test fun abstractPropertyInEnum() { 5678 val typeSpec = TypeSpec.enumBuilder("Bar") 5679 .addProperty( 5680 PropertySpec.builder("foo", Int::class) 5681 .addModifiers(ABSTRACT) 5682 .build(), 5683 ) 5684 .build() 5685 5686 assertThat(typeSpec.toString()).isEqualTo( 5687 """ 5688 |public enum class Bar { 5689 | ; 5690 | public abstract val foo: kotlin.Int 5691 |} 5692 | 5693 """.trimMargin(), 5694 ) 5695 } 5696 abstractPropertyInSealedClassnull5697 @Test fun abstractPropertyInSealedClass() { 5698 val typeSpec = TypeSpec.classBuilder("Bar") 5699 .addModifiers(SEALED) 5700 .addProperty( 5701 PropertySpec.builder("foo", Int::class) 5702 .addModifiers(ABSTRACT) 5703 .build(), 5704 ) 5705 .build() 5706 5707 assertThat(typeSpec.toString()).isEqualTo( 5708 """ 5709 |public sealed class Bar { 5710 | public abstract val foo: kotlin.Int 5711 |} 5712 | 5713 """.trimMargin(), 5714 ) 5715 } 5716 classKdocnull5717 @Test fun classKdoc() { 5718 val type = TypeSpec.classBuilder("MyClass") 5719 .addKdoc("This is my class") 5720 .primaryConstructor( 5721 FunSpec.constructorBuilder() 5722 .build(), 5723 ) 5724 .build() 5725 5726 //language=kotlin 5727 assertThat(type.toString()).isEqualTo( 5728 """ 5729 /** 5730 * This is my class 5731 */ 5732 public class MyClass() 5733 5734 """.trimIndent(), 5735 ) 5736 } 5737 5738 // https://github.com/square/kotlinpoet/issues/1630 primaryConstructorKDocnull5739 @Test fun primaryConstructorKDoc() { 5740 val type = TypeSpec.classBuilder("MyClass") 5741 .addKdoc("This is my class") 5742 .primaryConstructor( 5743 FunSpec.constructorBuilder() 5744 .addKdoc("This is my constructor") 5745 .build(), 5746 ) 5747 .build() 5748 5749 //language=kotlin 5750 assertThat(type.toString()).isEqualTo( 5751 """ 5752 /** 5753 * This is my class 5754 * 5755 * @constructor This is my constructor 5756 */ 5757 public class MyClass() 5758 5759 """.trimIndent(), 5760 ) 5761 } 5762 5763 // https://github.com/square/kotlinpoet/issues/1818 primaryConstructorCanNotDelegatenull5764 @Test fun primaryConstructorCanNotDelegate() { 5765 assertThrows<IllegalArgumentException> { 5766 TypeSpec.classBuilder("Child") 5767 .superclass(ClassName("com.squareup", "Parent")) 5768 .primaryConstructor( 5769 FunSpec.constructorBuilder() 5770 .callSuperConstructor(CodeBlock.of("%L", "param")) 5771 .addParameter( 5772 name = "param", 5773 type = ClassName("com.squareup", "Param"), 5774 ) 5775 .build(), 5776 ) 5777 .build() 5778 }.hasMessageThat().isEqualTo("primary constructor can't delegate to other constructors") 5779 } 5780 5781 companion object { 5782 private const val donutsPackage = "com.squareup.donuts" 5783 } 5784 } 5785