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.truth.Truth.assertThat 19 import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy 20 import java.util.Locale 21 import kotlin.test.Test 22 23 class CodeBlockTest { equalsAndHashCodenull24 @Test fun equalsAndHashCode() { 25 var a = CodeBlock.builder().build() 26 var b = CodeBlock.builder().build() 27 assertThat(a == b).isTrue() 28 assertThat(a.hashCode()).isEqualTo(b.hashCode()) 29 a = CodeBlock.builder().add("%L", "taco").build() 30 b = CodeBlock.builder().add("%L", "taco").build() 31 assertThat(a == b).isTrue() 32 assertThat(a.hashCode()).isEqualTo(b.hashCode()) 33 } 34 ofnull35 @Test fun of() { 36 val a = CodeBlock.of("%L taco", "delicious") 37 assertThat(a.toString()).isEqualTo("delicious taco") 38 } 39 doublePrecisionnull40 @Test fun doublePrecision() { 41 val doubles = listOf( 42 12345678900000.0 to "12_345_678_900_000.0", 43 12345678900000.07 to "12_345_678_900_000.07", 44 123456.0 to "123_456.0", 45 1234.5678 to "1_234.5678", 46 12.345678 to "12.345678", 47 0.12345678 to "0.12345678", 48 0.0001 to "0.0001", 49 0.00001 to "0.00001", 50 0.000001 to "0.000001", 51 0.0000001 to "0.0000001", 52 ) 53 for ((d, expected) in doubles) { 54 val a = CodeBlock.of("number %L", d) 55 assertThat(a.toString()).isEqualTo("number $expected") 56 } 57 } 58 floatPrecisionnull59 @Test fun floatPrecision() { 60 val floats = listOf( 61 12345678.0f to "12_345_678.0", 62 123456.0f to "123_456.0", 63 1234.567f to "1_234.567", 64 12.34567f to "12.34567", 65 0.1234567f to "0.1234567", 66 0.0001f to "0.0001", 67 0.00001f to "0.00001", 68 0.000001f to "0.000001", 69 0.0000001f to "0.0000001", 70 ) 71 for ((f, expected) in floats) { 72 val a = CodeBlock.of("number %L", f) 73 assertThat(a.toString()).isEqualTo("number $expected") 74 } 75 } 76 percentEscapeCannotBeIndexednull77 @Test fun percentEscapeCannotBeIndexed() { 78 assertThrows<IllegalArgumentException> { 79 CodeBlock.builder().add("%1%", "taco").build() 80 }.hasMessageThat().isEqualTo("%% may not have an index") 81 } 82 nameFormatCanBeIndexednull83 @Test fun nameFormatCanBeIndexed() { 84 val block = CodeBlock.builder().add("%1N", "taco").build() 85 assertThat(block.toString()).isEqualTo("taco") 86 } 87 literalFormatCanBeIndexednull88 @Test fun literalFormatCanBeIndexed() { 89 val block = CodeBlock.builder().add("%1L", "taco").build() 90 assertThat(block.toString()).isEqualTo("taco") 91 } 92 stringFormatCanBeIndexednull93 @Test fun stringFormatCanBeIndexed() { 94 val block = CodeBlock.builder().add("%1S", "taco").build() 95 assertThat(block.toString()).isEqualTo("\"taco\"") 96 } 97 typeFormatCanBeIndexednull98 @Test fun typeFormatCanBeIndexed() { 99 val block = CodeBlock.builder().add("%1T", String::class).build() 100 assertThat(block.toString()).isEqualTo("kotlin.String") 101 } 102 simpleNamedArgumentnull103 @Test fun simpleNamedArgument() { 104 val map = LinkedHashMap<String, Any>() 105 map["text"] = "taco" 106 val block = CodeBlock.builder().addNamed("%text:S", map).build() 107 assertThat(block.toString()).isEqualTo("\"taco\"") 108 } 109 repeatedNamedArgumentnull110 @Test fun repeatedNamedArgument() { 111 val map = LinkedHashMap<String, Any>() 112 map["text"] = "tacos" 113 val block = CodeBlock.builder() 114 .addNamed("\"I like \" + %text:S + \". Do you like \" + %text:S + \"?\"", map) 115 .build() 116 assertThat(block.toString()).isEqualTo( 117 "\"I like \" + \"tacos\" + \". Do you like \" + \"tacos\" + \"?\"", 118 ) 119 } 120 namedAndNoArgFormatnull121 @Test fun namedAndNoArgFormat() { 122 val map = LinkedHashMap<String, Any>() 123 map["text"] = "tacos" 124 val block = CodeBlock.builder() 125 .addNamed("⇥\n%text:L for\n⇤%%3.50", map).build() 126 assertThat(block.toString()).isEqualTo("\n tacos for\n%3.50") 127 } 128 missingNamedArgumentnull129 @Test fun missingNamedArgument() { 130 assertThrows<IllegalArgumentException> { 131 val map = LinkedHashMap<String, Any>() 132 CodeBlock.builder().addNamed("%text:S", map).build() 133 }.hasMessageThat().isEqualTo("Missing named argument for %text") 134 } 135 lowerCaseNamednull136 @Test fun lowerCaseNamed() { 137 assertThrows<IllegalArgumentException> { 138 val map = LinkedHashMap<String, Any>() 139 map["Text"] = "tacos" 140 CodeBlock.builder().addNamed("%Text:S", map).build() 141 }.hasMessageThat().isEqualTo("argument 'Text' must start with a lowercase character") 142 } 143 multipleNamedArgumentsnull144 @Test fun multipleNamedArguments() { 145 val map = LinkedHashMap<String, Any>() 146 map["pipe"] = System::class 147 map["text"] = "tacos" 148 149 val block = CodeBlock.builder() 150 .addNamed("%pipe:T.out.println(\"Let's eat some %text:L\");", map) 151 .build() 152 153 assertThat(block.toString()).isEqualTo( 154 "java.lang.System.out.println(\"Let's eat some tacos\");", 155 ) 156 } 157 namedNewlinenull158 @Test fun namedNewline() { 159 val map = LinkedHashMap<String, Any>() 160 map["clazz"] = java.lang.Integer::class 161 val block = CodeBlock.builder().addNamed("%clazz:T\n", map).build() 162 assertThat(block.toString()).isEqualTo("kotlin.Int\n") 163 } 164 danglingNamednull165 @Test fun danglingNamed() { 166 val map = LinkedHashMap<String, Any>() 167 map["clazz"] = Int::class 168 assertThrows<IllegalArgumentException> { 169 CodeBlock.builder().addNamed("%clazz:T%", map).build() 170 }.hasMessageThat().isEqualTo("dangling % at end") 171 } 172 indexTooHighnull173 @Test fun indexTooHigh() { 174 assertThrows<IllegalArgumentException> { 175 CodeBlock.builder().add("%2T", String::class).build() 176 }.hasMessageThat().isEqualTo("index 2 for '%2T' not in range (received 1 arguments)") 177 } 178 indexIsZeronull179 @Test fun indexIsZero() { 180 assertThrows<IllegalArgumentException> { 181 CodeBlock.builder().add("%0T", String::class).build() 182 }.hasMessageThat().isEqualTo("index 0 for '%0T' not in range (received 1 arguments)") 183 } 184 indexIsNegativenull185 @Test fun indexIsNegative() { 186 assertThrows<IllegalArgumentException> { 187 CodeBlock.builder().add("%-1T", String::class).build() 188 }.hasMessageThat().isEqualTo("invalid format string: '%-1T'") 189 } 190 indexWithoutFormatTypenull191 @Test fun indexWithoutFormatType() { 192 assertThrows<IllegalArgumentException> { 193 CodeBlock.builder().add("%1", String::class).build() 194 }.hasMessageThat().isEqualTo("dangling format characters in '%1'") 195 } 196 indexWithoutFormatTypeNotAtStringEndnull197 @Test fun indexWithoutFormatTypeNotAtStringEnd() { 198 assertThrows<IllegalArgumentException> { 199 CodeBlock.builder().add("%1 taco", String::class).build() 200 }.hasMessageThat().isEqualTo("invalid format string: '%1 taco'") 201 } 202 indexButNoArgumentsnull203 @Test fun indexButNoArguments() { 204 assertThrows<IllegalArgumentException> { 205 CodeBlock.builder().add("%1T").build() 206 }.hasMessageThat().isEqualTo("index 1 for '%1T' not in range (received 0 arguments)") 207 } 208 formatIndicatorAlonenull209 @Test fun formatIndicatorAlone() { 210 assertThrows<IllegalArgumentException> { 211 CodeBlock.builder().add("%", String::class).build() 212 }.hasMessageThat().isEqualTo("dangling format characters in '%'") 213 } 214 formatIndicatorWithoutIndexOrFormatTypenull215 @Test fun formatIndicatorWithoutIndexOrFormatType() { 216 assertThrows<IllegalArgumentException> { 217 CodeBlock.builder().add("% tacoString", String::class).build() 218 }.hasMessageThat().isEqualTo("invalid format string: '% tacoString'") 219 } 220 sameIndexCanBeUsedWithDifferentFormatsnull221 @Test fun sameIndexCanBeUsedWithDifferentFormats() { 222 val block = CodeBlock.builder() 223 .add("%1T.out.println(%1S)", System::class.asClassName()) 224 .build() 225 assertThat(block.toString()).isEqualTo("java.lang.System.out.println(\"java.lang.System\")") 226 } 227 tooManyStatementEntersnull228 @Test fun tooManyStatementEnters() { 229 val codeBlock = CodeBlock.builder() 230 .addStatement("print(«%L»)", "1 + 1") 231 .build() 232 assertThrows<IllegalStateException> { 233 // We can't report this error until rendering type because code blocks might be composed. 234 codeBlock.toString() 235 }.hasMessageThat().isEqualTo( 236 """ 237 |Can't open a new statement until the current statement is closed (opening « followed 238 |by another « without a closing »). 239 |Current code block: 240 |- Format parts: [«, print(, «, %L, », ), \n, »] 241 |- Arguments: [1 + 1] 242 | 243 """.trimMargin(), 244 ) 245 } 246 statementExitWithoutStatementEnternull247 @Test fun statementExitWithoutStatementEnter() { 248 val codeBlock = CodeBlock.builder() 249 .addStatement("print(%L»)", "1 + 1") 250 .build() 251 assertThrows<IllegalStateException> { 252 // We can't report this error until rendering type because code blocks might be composed. 253 codeBlock.toString() 254 }.hasMessageThat().isEqualTo( 255 """ 256 |Can't close a statement that hasn't been opened (closing » is not preceded by an 257 |opening «). 258 |Current code block: 259 |- Format parts: [«, print(, %L, », ), \n, »] 260 |- Arguments: [1 + 1] 261 | 262 """.trimMargin(), 263 ) 264 } 265 nullableTypenull266 @Test fun nullableType() { 267 val type = String::class.asTypeName().copy(nullable = true) 268 val typeBlock = CodeBlock.of("%T", type) 269 assertThat(typeBlock.toString()).isEqualTo("kotlin.String?") 270 271 val list = (List::class.asClassName().copy(nullable = true) as ClassName) 272 .parameterizedBy(Int::class.asTypeName().copy(nullable = true)) 273 .copy(nullable = true) 274 val listBlock = CodeBlock.of("%T", list) 275 assertThat(listBlock.toString()).isEqualTo("kotlin.collections.List<kotlin.Int?>?") 276 277 val map = (Map::class.asClassName().copy(nullable = true) as ClassName) 278 .parameterizedBy(String::class.asTypeName().copy(nullable = true), list) 279 .copy(nullable = true) 280 val mapBlock = CodeBlock.of("%T", map) 281 assertThat(mapBlock.toString()) 282 .isEqualTo("kotlin.collections.Map<kotlin.String?, kotlin.collections.List<kotlin.Int?>?>?") 283 284 val rarr = WildcardTypeName.producerOf(String::class.asTypeName().copy(nullable = true)) 285 val rarrBlock = CodeBlock.of("%T", rarr) 286 assertThat(rarrBlock.toString()).isEqualTo("out kotlin.String?") 287 } 288 withoutPrefixMatchingnull289 @Test fun withoutPrefixMatching() { 290 assertThat( 291 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 292 .withoutPrefix(CodeBlock.of("")), 293 ) 294 .isEqualTo(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")) 295 assertThat( 296 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 297 .withoutPrefix(CodeBlock.of("ab")), 298 ) 299 .isEqualTo(CodeBlock.of("cd %S efgh %S ijkl", "x", "y")) 300 assertThat( 301 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 302 .withoutPrefix(CodeBlock.of("abcd ")), 303 ) 304 .isEqualTo(CodeBlock.of("%S efgh %S ijkl", "x", "y")) 305 assertThat( 306 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 307 .withoutPrefix(CodeBlock.of("abcd %S", "x")), 308 ) 309 .isEqualTo(CodeBlock.of(" efgh %S ijkl", "y")) 310 assertThat( 311 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 312 .withoutPrefix(CodeBlock.of("abcd %S ef", "x")), 313 ) 314 .isEqualTo(CodeBlock.of("gh %S ijkl", "y")) 315 assertThat( 316 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 317 .withoutPrefix(CodeBlock.of("abcd %S efgh ", "x")), 318 ) 319 .isEqualTo(CodeBlock.of("%S ijkl", "y")) 320 assertThat( 321 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 322 .withoutPrefix(CodeBlock.of("abcd %S efgh %S", "x", "y")), 323 ) 324 .isEqualTo(CodeBlock.of(" ijkl")) 325 assertThat( 326 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 327 .withoutPrefix(CodeBlock.of("abcd %S efgh %S ij", "x", "y")), 328 ) 329 .isEqualTo(CodeBlock.of("kl")) 330 assertThat( 331 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 332 .withoutPrefix(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")), 333 ) 334 .isEqualTo(CodeBlock.of("")) 335 } 336 withoutPrefixNoArgsnull337 @Test fun withoutPrefixNoArgs() { 338 assertThat( 339 CodeBlock.of("abcd %% efgh %% ijkl") 340 .withoutPrefix(CodeBlock.of("")), 341 ) 342 .isEqualTo(CodeBlock.of("abcd %% efgh %% ijkl")) 343 assertThat( 344 CodeBlock.of("abcd %% efgh %% ijkl") 345 .withoutPrefix(CodeBlock.of("ab")), 346 ) 347 .isEqualTo(CodeBlock.of("cd %% efgh %% ijkl")) 348 assertThat( 349 CodeBlock.of("abcd %% efgh %% ijkl") 350 .withoutPrefix(CodeBlock.of("abcd ")), 351 ) 352 .isEqualTo(CodeBlock.of("%% efgh %% ijkl")) 353 assertThat( 354 CodeBlock.of("abcd %% efgh %% ijkl") 355 .withoutPrefix(CodeBlock.of("abcd %%")), 356 ) 357 .isEqualTo(CodeBlock.of(" efgh %% ijkl")) 358 assertThat( 359 CodeBlock.of("abcd %% efgh %% ijkl") 360 .withoutPrefix(CodeBlock.of("abcd %% ef")), 361 ) 362 .isEqualTo(CodeBlock.of("gh %% ijkl")) 363 assertThat( 364 CodeBlock.of("abcd %% efgh %% ijkl") 365 .withoutPrefix(CodeBlock.of("abcd %% efgh ")), 366 ) 367 .isEqualTo(CodeBlock.of("%% ijkl")) 368 assertThat( 369 CodeBlock.of("abcd %% efgh %% ijkl") 370 .withoutPrefix(CodeBlock.of("abcd %% efgh %%")), 371 ) 372 .isEqualTo(CodeBlock.of(" ijkl")) 373 assertThat( 374 CodeBlock.of("abcd %% efgh %% ijkl") 375 .withoutPrefix(CodeBlock.of("abcd %% efgh %% ij")), 376 ) 377 .isEqualTo(CodeBlock.of("kl")) 378 assertThat( 379 CodeBlock.of("abcd %% efgh %% ijkl") 380 .withoutPrefix(CodeBlock.of("abcd %% efgh %% ijkl")), 381 ) 382 .isEqualTo(CodeBlock.of("")) 383 } 384 withoutPrefixArgMismatchnull385 @Test fun withoutPrefixArgMismatch() { 386 assertThat( 387 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 388 .withoutPrefix(CodeBlock.of("abcd %S efgh %S ij", "x", "z")), 389 ) 390 .isNull() 391 assertThat( 392 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 393 .withoutPrefix(CodeBlock.of("abcd %S efgh %S ij", "z", "y")), 394 ) 395 .isNull() 396 } 397 withoutPrefixFormatPartMismatchnull398 @Test fun withoutPrefixFormatPartMismatch() { 399 assertThat( 400 CodeBlock.of("abcd %S efgh %S ijkl", "x", "y") 401 .withoutPrefix(CodeBlock.of("abcd %S efgx %S ij", "x", "y")), 402 ) 403 .isNull() 404 assertThat( 405 CodeBlock.of("abcd %S efgh %% ijkl", "x") 406 .withoutPrefix(CodeBlock.of("abcd %% efgh %S ij", "x")), 407 ) 408 .isNull() 409 } 410 withoutPrefixTooShortnull411 @Test fun withoutPrefixTooShort() { 412 assertThat( 413 CodeBlock.of("abcd %S efgh %S", "x", "y") 414 .withoutPrefix(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")), 415 ) 416 .isNull() 417 assertThat( 418 CodeBlock.of("abcd %S efgh", "x") 419 .withoutPrefix(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")), 420 ) 421 .isNull() 422 } 423 trimEmptynull424 @Test fun trimEmpty() { 425 assertThat(CodeBlock.of("").trim()) 426 .isEqualTo(CodeBlock.of("")) 427 } 428 trimNoPlaceholdersnull429 @Test fun trimNoPlaceholders() { 430 assertThat(CodeBlock.of("return null").trim()) 431 .isEqualTo(CodeBlock.of("return null")) 432 } 433 trimPlaceholdersWithArgsnull434 @Test fun trimPlaceholdersWithArgs() { 435 assertThat(CodeBlock.of("return %S", "taco").trim()) 436 .isEqualTo(CodeBlock.of("return %S", "taco")) 437 } 438 trimNoArgPlaceholderMiddlenull439 @Test fun trimNoArgPlaceholderMiddle() { 440 assertThat(CodeBlock.of("this.taco = %S", "taco").trim()) 441 .isEqualTo(CodeBlock.of("this.taco = %S", "taco")) 442 } 443 trimNoArgPlaceholderStartnull444 @Test fun trimNoArgPlaceholderStart() { 445 assertThat(CodeBlock.of("⇥return ").trim()) 446 .isEqualTo(CodeBlock.of("return ")) 447 } 448 trimNoArgPlaceholderEndnull449 @Test fun trimNoArgPlaceholderEnd() { 450 assertThat(CodeBlock.of("return ⇥").trim()) 451 .isEqualTo(CodeBlock.of("return ")) 452 } 453 trimNoArgPlaceholdersStartEndnull454 @Test fun trimNoArgPlaceholdersStartEnd() { 455 assertThat(CodeBlock.of("«return this»").trim()) 456 .isEqualTo(CodeBlock.of("return this")) 457 } 458 trimMultipleNoArgPlaceholdersnull459 @Test fun trimMultipleNoArgPlaceholders() { 460 assertThat( 461 CodeBlock.of("«return if (x > %L) %S else %S»", 1, "a", "b").trim(), 462 ) 463 .isEqualTo(CodeBlock.of("return if (x > %L) %S else %S", 1, "a", "b")) 464 } 465 trimOnlyNoArgPlaceholdersnull466 @Test fun trimOnlyNoArgPlaceholders() { 467 assertThat(CodeBlock.of("«»⇥⇤").trim()) 468 .isEqualTo(CodeBlock.of("")) 469 } 470 replaceSimplenull471 @Test fun replaceSimple() { 472 assertThat(CodeBlock.of("%%⇥%%").replaceAll("%%", "")) 473 .isEqualTo(CodeBlock.of("⇥")) 474 } 475 replaceNoMatchesnull476 @Test fun replaceNoMatches() { 477 assertThat(CodeBlock.of("%%⇥%%").replaceAll("⇤", "")) 478 .isEqualTo(CodeBlock.of("%%⇥%%")) 479 } 480 replaceRegexnull481 @Test fun replaceRegex() { 482 assertThat(CodeBlock.of("%%⇥%%⇤").replaceAll("[⇥|⇤]", "")) 483 .isEqualTo(CodeBlock.of("%%%%")) 484 } 485 joinToCodenull486 @Test fun joinToCode() { 487 val blocks = listOf(CodeBlock.of("%L", "taco1"), CodeBlock.of("%L", "taco2"), CodeBlock.of("%L", "taco3")) 488 assertThat(blocks.joinToCode(prefix = "(", suffix = ")")) 489 .isEqualTo(CodeBlock.of("(%L, %L, %L)", "taco1", "taco2", "taco3")) 490 } 491 joinToCodeTransformnull492 @Test fun joinToCodeTransform() { 493 val blocks = listOf("taco1", "taco2", "taco3") 494 assertThat(blocks.joinToCode { CodeBlock.of("%S", it) }) 495 .isEqualTo(CodeBlock.of("%S, %S, %S", "taco1", "taco2", "taco3")) 496 } 497 beginControlFlowWithParamsnull498 @Test fun beginControlFlowWithParams() { 499 val controlFlow = CodeBlock.builder() 500 .beginControlFlow("list.forEach { element ->") 501 .addStatement("println(element)") 502 .endControlFlow() 503 .build() 504 assertThat(controlFlow.toString()).isEqualTo( 505 """ 506 |list.forEach { element -> 507 | println(element) 508 |} 509 | 510 """.trimMargin(), 511 ) 512 } 513 beginControlFlowWithParamsAndTemplateStringnull514 @Test fun beginControlFlowWithParamsAndTemplateString() { 515 val controlFlow = CodeBlock.builder() 516 .beginControlFlow("listOf(\"\${1.toString()}\").forEach { element ->") 517 .addStatement("println(element)") 518 .endControlFlow() 519 .build() 520 assertThat(controlFlow.toString()).isEqualTo( 521 """ 522 |listOf("${'$'}{1.toString()}").forEach { element -> 523 | println(element) 524 |} 525 | 526 """.trimMargin(), 527 ) 528 } 529 buildCodeBlocknull530 @Test fun buildCodeBlock() { 531 val codeBlock = buildCodeBlock { 532 beginControlFlow("if (2 == 2)") 533 addStatement("println(%S)", "foo") 534 nextControlFlow("else") 535 addStatement("println(%S)", "bar") 536 endControlFlow() 537 } 538 assertThat(codeBlock.toString()).isEqualTo( 539 """ 540 |if (2 == 2) { 541 | println("foo") 542 |} else { 543 | println("bar") 544 |} 545 | 546 """.trimMargin(), 547 ) 548 } 549 nonWrappingControlFlownull550 @Test fun nonWrappingControlFlow() { 551 val file = FileSpec.builder("com.squareup.tacos", "Test") 552 .addFunction( 553 FunSpec.builder("test") 554 .beginControlFlow("if (%1S == %1S)", "Very long string that would wrap the line ") 555 .nextControlFlow("else if (%1S == %1S)", "Long string that would wrap the line 2 ") 556 .endControlFlow() 557 .build(), 558 ) 559 .build() 560 assertThat(file.toString()).isEqualTo( 561 """ 562 |package com.squareup.tacos 563 | 564 |public fun test() { 565 | if ("Very long string that would wrap the line " == 566 | "Very long string that would wrap the line ") { 567 | } else if ("Long string that would wrap the line 2 " == 568 | "Long string that would wrap the line 2 ") { 569 | } 570 |} 571 | 572 """.trimMargin(), 573 ) 574 } 575 ensureEndsWithNewLineWithNoArgsnull576 @Test fun ensureEndsWithNewLineWithNoArgs() { 577 val codeBlock = CodeBlock.builder() 578 .addStatement("Modeling a kdoc") 579 .add("\n") 580 .addStatement("Statement with no args") 581 .build() 582 583 assertThat(codeBlock.ensureEndsWithNewLine().toString()).isEqualTo( 584 """ 585 |Modeling a kdoc 586 | 587 |Statement with no args 588 | 589 """.trimMargin(), 590 ) 591 } 592 N escapes keywordsnull593 @Test fun `N escapes keywords`() { 594 val funSpec = FunSpec.builder("object").build() 595 assertThat(CodeBlock.of("%N", funSpec).toString()).isEqualTo("`object`") 596 } 597 N escapes spacesnull598 @Test fun `N escapes spaces`() { 599 val funSpec = FunSpec.builder("create taco").build() 600 assertThat(CodeBlock.of("%N", funSpec).toString()).isEqualTo("`create taco`") 601 } 602 clearnull603 @Test fun clear() { 604 val blockBuilder = CodeBlock.builder().addStatement("%S is some existing code", "This") 605 606 blockBuilder.clear() 607 608 assertThat(blockBuilder.build().toString()).isEmpty() 609 } 610 withIndentnull611 @Test fun withIndent() { 612 val codeBlock = CodeBlock.Builder() 613 .apply { 614 addStatement("User(") 615 withIndent { 616 addStatement("age = 42,") 617 addStatement("cities = listOf(") 618 withIndent { 619 addStatement("%S,", "Berlin") 620 addStatement("%S,", "London") 621 } 622 addStatement(")") 623 } 624 addStatement(")") 625 } 626 .build() 627 628 assertThat(codeBlock.toString()).isEqualTo( 629 """ 630 |User( 631 | age = 42, 632 | cities = listOf( 633 | "Berlin", 634 | "London", 635 | ) 636 |) 637 | 638 """.trimMargin(), 639 ) 640 } 641 642 // https://github.com/square/kotlinpoet/issues/1236 dontEscapeBackslashesInRawStringsnull643 @Test fun dontEscapeBackslashesInRawStrings() { 644 // println("ESCAPE '\\'") -> ESCAPE '\' 645 assertThat(CodeBlock.of("%S", "ESCAPE '\\'").toString()).isEqualTo("\"ESCAPE '\\\\'\"") 646 // println("""ESCAPE '\'""") -> ESCAPE '\' 647 assertThat(CodeBlock.of("%P", """ESCAPE '\'""").toString()).isEqualTo("\"\"\"ESCAPE '\\'\"\"\"") 648 } 649 650 // https://github.com/square/kotlinpoet/issues/1381 useUnderscoresOnLargeDecimalLiteralsnull651 @Test fun useUnderscoresOnLargeDecimalLiterals() { 652 assertThat(CodeBlock.of("%L", 10000).toString()).isEqualTo("10_000") 653 assertThat(CodeBlock.of("%L", 100000L).toString()).isEqualTo("100_000") 654 assertThat(CodeBlock.of("%L", Int.MIN_VALUE).toString()).isEqualTo("-2_147_483_648") 655 assertThat(CodeBlock.of("%L", Int.MAX_VALUE).toString()).isEqualTo("2_147_483_647") 656 assertThat(CodeBlock.of("%L", Long.MIN_VALUE).toString()).isEqualTo("-9_223_372_036_854_775_808") 657 assertThat(CodeBlock.of("%L", 10000.123).toString()).isEqualTo("10_000.123") 658 assertThat(CodeBlock.of("%L", 3.0).toString()).isEqualTo("3.0") 659 assertThat(CodeBlock.of("%L", 10000.123f).toString()).isEqualTo("10_000.123") 660 assertThat(CodeBlock.of("%L", 10000.123456789012).toString()).isEqualTo("10_000.123456789011") 661 assertThat(CodeBlock.of("%L", 1281.toShort()).toString()).isEqualTo("1_281") 662 663 assertThat(CodeBlock.of("%S", 10000).toString()).isEqualTo("\"10000\"") 664 assertThat(CodeBlock.of("%S", 100000L).toString()).isEqualTo("\"100000\"") 665 assertThat(CodeBlock.of("%S", Int.MIN_VALUE).toString()).isEqualTo("\"-2147483648\"") 666 assertThat(CodeBlock.of("%S", Int.MAX_VALUE).toString()).isEqualTo("\"2147483647\"") 667 assertThat(CodeBlock.of("%S", Long.MIN_VALUE).toString()).isEqualTo("\"-9223372036854775808\"") 668 assertThat(CodeBlock.of("%S", 10000.123).toString()).isEqualTo("\"10000.123\"") 669 assertThat(CodeBlock.of("%S", 3.0).toString()).isEqualTo("\"3.0\"") 670 assertThat(CodeBlock.of("%S", 10000.123f).toString()).isEqualTo("\"10000.123\"") 671 assertThat(CodeBlock.of("%S", 10000.12345678901).toString()).isEqualTo("\"10000.12345678901\"") 672 assertThat(CodeBlock.of("%S", 1281.toShort()).toString()).isEqualTo("\"1281\"") 673 } 674 675 // https://github.com/square/kotlinpoet/issues/1657 minusSignInSwedishLocalenull676 @Test fun minusSignInSwedishLocale() { 677 val defaultLocale = Locale.getDefault() 678 Locale.setDefault(Locale.forLanguageTag("sv")) 679 680 val i = -42 681 val s = CodeBlock.of("val i = %L", i) 682 assertThat(s.toString()).isEqualTo("val i = -42") 683 684 Locale.setDefault(defaultLocale) 685 } 686 } 687