• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 kotlin.test.Test
21 
22 class CodeBlockTest {
equalsAndHashCodenull23   @Test fun equalsAndHashCode() {
24     var a = CodeBlock.builder().build()
25     var b = CodeBlock.builder().build()
26     assertThat(a == b).isTrue()
27     assertThat(a.hashCode()).isEqualTo(b.hashCode())
28     a = CodeBlock.builder().add("%L", "taco").build()
29     b = CodeBlock.builder().add("%L", "taco").build()
30     assertThat(a == b).isTrue()
31     assertThat(a.hashCode()).isEqualTo(b.hashCode())
32   }
33 
ofnull34   @Test fun of() {
35     val a = CodeBlock.of("%L taco", "delicious")
36     assertThat(a.toString()).isEqualTo("delicious taco")
37   }
38 
percentEscapeCannotBeIndexednull39   @Test fun percentEscapeCannotBeIndexed() {
40     assertThrows<IllegalArgumentException> {
41       CodeBlock.builder().add("%1%", "taco").build()
42     }.hasMessageThat().isEqualTo("%% may not have an index")
43   }
44 
nameFormatCanBeIndexednull45   @Test fun nameFormatCanBeIndexed() {
46     val block = CodeBlock.builder().add("%1N", "taco").build()
47     assertThat(block.toString()).isEqualTo("taco")
48   }
49 
literalFormatCanBeIndexednull50   @Test fun literalFormatCanBeIndexed() {
51     val block = CodeBlock.builder().add("%1L", "taco").build()
52     assertThat(block.toString()).isEqualTo("taco")
53   }
54 
stringFormatCanBeIndexednull55   @Test fun stringFormatCanBeIndexed() {
56     val block = CodeBlock.builder().add("%1S", "taco").build()
57     assertThat(block.toString()).isEqualTo("\"taco\"")
58   }
59 
typeFormatCanBeIndexednull60   @Test fun typeFormatCanBeIndexed() {
61     val block = CodeBlock.builder().add("%1T", String::class).build()
62     assertThat(block.toString()).isEqualTo("kotlin.String")
63   }
64 
simpleNamedArgumentnull65   @Test fun simpleNamedArgument() {
66     val map = LinkedHashMap<String, Any>()
67     map["text"] = "taco"
68     val block = CodeBlock.builder().addNamed("%text:S", map).build()
69     assertThat(block.toString()).isEqualTo("\"taco\"")
70   }
71 
repeatedNamedArgumentnull72   @Test fun repeatedNamedArgument() {
73     val map = LinkedHashMap<String, Any>()
74     map["text"] = "tacos"
75     val block = CodeBlock.builder()
76       .addNamed("\"I like \" + %text:S + \". Do you like \" + %text:S + \"?\"", map)
77       .build()
78     assertThat(block.toString()).isEqualTo(
79       "\"I like \" + \"tacos\" + \". Do you like \" + \"tacos\" + \"?\"",
80     )
81   }
82 
namedAndNoArgFormatnull83   @Test fun namedAndNoArgFormat() {
84     val map = LinkedHashMap<String, Any>()
85     map["text"] = "tacos"
86     val block = CodeBlock.builder()
87       .addNamed("⇥\n%text:L for\n⇤%%3.50", map).build()
88     assertThat(block.toString()).isEqualTo("\n  tacos for\n%3.50")
89   }
90 
missingNamedArgumentnull91   @Test fun missingNamedArgument() {
92     assertThrows<IllegalArgumentException> {
93       val map = LinkedHashMap<String, Any>()
94       CodeBlock.builder().addNamed("%text:S", map).build()
95     }.hasMessageThat().isEqualTo("Missing named argument for %text")
96   }
97 
lowerCaseNamednull98   @Test fun lowerCaseNamed() {
99     assertThrows<IllegalArgumentException> {
100       val map = LinkedHashMap<String, Any>()
101       map["Text"] = "tacos"
102       CodeBlock.builder().addNamed("%Text:S", map).build()
103     }.hasMessageThat().isEqualTo("argument 'Text' must start with a lowercase character")
104   }
105 
multipleNamedArgumentsnull106   @Test fun multipleNamedArguments() {
107     val map = LinkedHashMap<String, Any>()
108     map["pipe"] = System::class
109     map["text"] = "tacos"
110 
111     val block = CodeBlock.builder()
112       .addNamed("%pipe:T.out.println(\"Let's eat some %text:L\");", map)
113       .build()
114 
115     assertThat(block.toString()).isEqualTo(
116       "java.lang.System.out.println(\"Let's eat some tacos\");",
117     )
118   }
119 
namedNewlinenull120   @Test fun namedNewline() {
121     val map = LinkedHashMap<String, Any>()
122     map["clazz"] = java.lang.Integer::class
123     val block = CodeBlock.builder().addNamed("%clazz:T\n", map).build()
124     assertThat(block.toString()).isEqualTo("kotlin.Int\n")
125   }
126 
danglingNamednull127   @Test fun danglingNamed() {
128     val map = LinkedHashMap<String, Any>()
129     map["clazz"] = Int::class
130     assertThrows<IllegalArgumentException> {
131       CodeBlock.builder().addNamed("%clazz:T%", map).build()
132     }.hasMessageThat().isEqualTo("dangling % at end")
133   }
134 
indexTooHighnull135   @Test fun indexTooHigh() {
136     assertThrows<IllegalArgumentException> {
137       CodeBlock.builder().add("%2T", String::class).build()
138     }.hasMessageThat().isEqualTo("index 2 for '%2T' not in range (received 1 arguments)")
139   }
140 
indexIsZeronull141   @Test fun indexIsZero() {
142     assertThrows<IllegalArgumentException> {
143       CodeBlock.builder().add("%0T", String::class).build()
144     }.hasMessageThat().isEqualTo("index 0 for '%0T' not in range (received 1 arguments)")
145   }
146 
indexIsNegativenull147   @Test fun indexIsNegative() {
148     assertThrows<IllegalArgumentException> {
149       CodeBlock.builder().add("%-1T", String::class).build()
150     }.hasMessageThat().isEqualTo("invalid format string: '%-1T'")
151   }
152 
indexWithoutFormatTypenull153   @Test fun indexWithoutFormatType() {
154     assertThrows<IllegalArgumentException> {
155       CodeBlock.builder().add("%1", String::class).build()
156     }.hasMessageThat().isEqualTo("dangling format characters in '%1'")
157   }
158 
indexWithoutFormatTypeNotAtStringEndnull159   @Test fun indexWithoutFormatTypeNotAtStringEnd() {
160     assertThrows<IllegalArgumentException> {
161       CodeBlock.builder().add("%1 taco", String::class).build()
162     }.hasMessageThat().isEqualTo("invalid format string: '%1 taco'")
163   }
164 
indexButNoArgumentsnull165   @Test fun indexButNoArguments() {
166     assertThrows<IllegalArgumentException> {
167       CodeBlock.builder().add("%1T").build()
168     }.hasMessageThat().isEqualTo("index 1 for '%1T' not in range (received 0 arguments)")
169   }
170 
formatIndicatorAlonenull171   @Test fun formatIndicatorAlone() {
172     assertThrows<IllegalArgumentException> {
173       CodeBlock.builder().add("%", String::class).build()
174     }.hasMessageThat().isEqualTo("dangling format characters in '%'")
175   }
176 
formatIndicatorWithoutIndexOrFormatTypenull177   @Test fun formatIndicatorWithoutIndexOrFormatType() {
178     assertThrows<IllegalArgumentException> {
179       CodeBlock.builder().add("% tacoString", String::class).build()
180     }.hasMessageThat().isEqualTo("invalid format string: '% tacoString'")
181   }
182 
sameIndexCanBeUsedWithDifferentFormatsnull183   @Test fun sameIndexCanBeUsedWithDifferentFormats() {
184     val block = CodeBlock.builder()
185       .add("%1T.out.println(%1S)", System::class.asClassName())
186       .build()
187     assertThat(block.toString()).isEqualTo("java.lang.System.out.println(\"java.lang.System\")")
188   }
189 
tooManyStatementEntersnull190   @Test fun tooManyStatementEnters() {
191     val codeBlock = CodeBlock.builder()
192       .addStatement("print(«%L»)", "1 + 1")
193       .build()
194     assertThrows<IllegalStateException> {
195       // We can't report this error until rendering type because code blocks might be composed.
196       codeBlock.toString()
197     }.hasMessageThat().isEqualTo(
198       """
199       |Can't open a new statement until the current statement is closed (opening « followed
200       |by another « without a closing »).
201       |Current code block:
202       |- Format parts: [«, print(, «, %L, », ), \n, »]
203       |- Arguments: [1 + 1]
204       |
205       """.trimMargin(),
206     )
207   }
208 
statementExitWithoutStatementEnternull209   @Test fun statementExitWithoutStatementEnter() {
210     val codeBlock = CodeBlock.builder()
211       .addStatement("print(%L»)", "1 + 1")
212       .build()
213     assertThrows<IllegalStateException> {
214       // We can't report this error until rendering type because code blocks might be composed.
215       codeBlock.toString()
216     }.hasMessageThat().isEqualTo(
217       """
218       |Can't close a statement that hasn't been opened (closing » is not preceded by an
219       |opening «).
220       |Current code block:
221       |- Format parts: [«, print(, %L, », ), \n, »]
222       |- Arguments: [1 + 1]
223       |
224       """.trimMargin(),
225     )
226   }
227 
nullableTypenull228   @Test fun nullableType() {
229     val type = String::class.asTypeName().copy(nullable = true)
230     val typeBlock = CodeBlock.of("%T", type)
231     assertThat(typeBlock.toString()).isEqualTo("kotlin.String?")
232 
233     val list = (List::class.asClassName().copy(nullable = true) as ClassName)
234       .parameterizedBy(Int::class.asTypeName().copy(nullable = true))
235       .copy(nullable = true)
236     val listBlock = CodeBlock.of("%T", list)
237     assertThat(listBlock.toString()).isEqualTo("kotlin.collections.List<kotlin.Int?>?")
238 
239     val map = (Map::class.asClassName().copy(nullable = true) as ClassName)
240       .parameterizedBy(String::class.asTypeName().copy(nullable = true), list)
241       .copy(nullable = true)
242     val mapBlock = CodeBlock.of("%T", map)
243     assertThat(mapBlock.toString())
244       .isEqualTo("kotlin.collections.Map<kotlin.String?, kotlin.collections.List<kotlin.Int?>?>?")
245 
246     val rarr = WildcardTypeName.producerOf(String::class.asTypeName().copy(nullable = true))
247     val rarrBlock = CodeBlock.of("%T", rarr)
248     assertThat(rarrBlock.toString()).isEqualTo("out kotlin.String?")
249   }
250 
withoutPrefixMatchingnull251   @Test fun withoutPrefixMatching() {
252     assertThat(
253       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
254         .withoutPrefix(CodeBlock.of("")),
255     )
256       .isEqualTo(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y"))
257     assertThat(
258       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
259         .withoutPrefix(CodeBlock.of("ab")),
260     )
261       .isEqualTo(CodeBlock.of("cd %S efgh %S ijkl", "x", "y"))
262     assertThat(
263       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
264         .withoutPrefix(CodeBlock.of("abcd ")),
265     )
266       .isEqualTo(CodeBlock.of("%S efgh %S ijkl", "x", "y"))
267     assertThat(
268       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
269         .withoutPrefix(CodeBlock.of("abcd %S", "x")),
270     )
271       .isEqualTo(CodeBlock.of(" efgh %S ijkl", "y"))
272     assertThat(
273       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
274         .withoutPrefix(CodeBlock.of("abcd %S ef", "x")),
275     )
276       .isEqualTo(CodeBlock.of("gh %S ijkl", "y"))
277     assertThat(
278       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
279         .withoutPrefix(CodeBlock.of("abcd %S efgh ", "x")),
280     )
281       .isEqualTo(CodeBlock.of("%S ijkl", "y"))
282     assertThat(
283       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
284         .withoutPrefix(CodeBlock.of("abcd %S efgh %S", "x", "y")),
285     )
286       .isEqualTo(CodeBlock.of(" ijkl"))
287     assertThat(
288       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
289         .withoutPrefix(CodeBlock.of("abcd %S efgh %S ij", "x", "y")),
290     )
291       .isEqualTo(CodeBlock.of("kl"))
292     assertThat(
293       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
294         .withoutPrefix(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")),
295     )
296       .isEqualTo(CodeBlock.of(""))
297   }
298 
withoutPrefixNoArgsnull299   @Test fun withoutPrefixNoArgs() {
300     assertThat(
301       CodeBlock.of("abcd %% efgh %% ijkl")
302         .withoutPrefix(CodeBlock.of("")),
303     )
304       .isEqualTo(CodeBlock.of("abcd %% efgh %% ijkl"))
305     assertThat(
306       CodeBlock.of("abcd %% efgh %% ijkl")
307         .withoutPrefix(CodeBlock.of("ab")),
308     )
309       .isEqualTo(CodeBlock.of("cd %% efgh %% ijkl"))
310     assertThat(
311       CodeBlock.of("abcd %% efgh %% ijkl")
312         .withoutPrefix(CodeBlock.of("abcd ")),
313     )
314       .isEqualTo(CodeBlock.of("%% efgh %% ijkl"))
315     assertThat(
316       CodeBlock.of("abcd %% efgh %% ijkl")
317         .withoutPrefix(CodeBlock.of("abcd %%")),
318     )
319       .isEqualTo(CodeBlock.of(" efgh %% ijkl"))
320     assertThat(
321       CodeBlock.of("abcd %% efgh %% ijkl")
322         .withoutPrefix(CodeBlock.of("abcd %% ef")),
323     )
324       .isEqualTo(CodeBlock.of("gh %% ijkl"))
325     assertThat(
326       CodeBlock.of("abcd %% efgh %% ijkl")
327         .withoutPrefix(CodeBlock.of("abcd %% efgh ")),
328     )
329       .isEqualTo(CodeBlock.of("%% ijkl"))
330     assertThat(
331       CodeBlock.of("abcd %% efgh %% ijkl")
332         .withoutPrefix(CodeBlock.of("abcd %% efgh %%")),
333     )
334       .isEqualTo(CodeBlock.of(" ijkl"))
335     assertThat(
336       CodeBlock.of("abcd %% efgh %% ijkl")
337         .withoutPrefix(CodeBlock.of("abcd %% efgh %% ij")),
338     )
339       .isEqualTo(CodeBlock.of("kl"))
340     assertThat(
341       CodeBlock.of("abcd %% efgh %% ijkl")
342         .withoutPrefix(CodeBlock.of("abcd %% efgh %% ijkl")),
343     )
344       .isEqualTo(CodeBlock.of(""))
345   }
346 
withoutPrefixArgMismatchnull347   @Test fun withoutPrefixArgMismatch() {
348     assertThat(
349       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
350         .withoutPrefix(CodeBlock.of("abcd %S efgh %S ij", "x", "z")),
351     )
352       .isNull()
353     assertThat(
354       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
355         .withoutPrefix(CodeBlock.of("abcd %S efgh %S ij", "z", "y")),
356     )
357       .isNull()
358   }
359 
withoutPrefixFormatPartMismatchnull360   @Test fun withoutPrefixFormatPartMismatch() {
361     assertThat(
362       CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")
363         .withoutPrefix(CodeBlock.of("abcd %S efgx %S ij", "x", "y")),
364     )
365       .isNull()
366     assertThat(
367       CodeBlock.of("abcd %S efgh %% ijkl", "x")
368         .withoutPrefix(CodeBlock.of("abcd %% efgh %S ij", "x")),
369     )
370       .isNull()
371   }
372 
withoutPrefixTooShortnull373   @Test fun withoutPrefixTooShort() {
374     assertThat(
375       CodeBlock.of("abcd %S efgh %S", "x", "y")
376         .withoutPrefix(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")),
377     )
378       .isNull()
379     assertThat(
380       CodeBlock.of("abcd %S efgh", "x")
381         .withoutPrefix(CodeBlock.of("abcd %S efgh %S ijkl", "x", "y")),
382     )
383       .isNull()
384   }
385 
trimEmptynull386   @Test fun trimEmpty() {
387     assertThat(CodeBlock.of("").trim())
388       .isEqualTo(CodeBlock.of(""))
389   }
390 
trimNoPlaceholdersnull391   @Test fun trimNoPlaceholders() {
392     assertThat(CodeBlock.of("return null").trim())
393       .isEqualTo(CodeBlock.of("return null"))
394   }
395 
trimPlaceholdersWithArgsnull396   @Test fun trimPlaceholdersWithArgs() {
397     assertThat(CodeBlock.of("return %S", "taco").trim())
398       .isEqualTo(CodeBlock.of("return %S", "taco"))
399   }
400 
trimNoArgPlaceholderMiddlenull401   @Test fun trimNoArgPlaceholderMiddle() {
402     assertThat(CodeBlock.of("this.taco = %S", "taco").trim())
403       .isEqualTo(CodeBlock.of("this.taco = %S", "taco"))
404   }
405 
trimNoArgPlaceholderStartnull406   @Test fun trimNoArgPlaceholderStart() {
407     assertThat(CodeBlock.of("⇥return ").trim())
408       .isEqualTo(CodeBlock.of("return "))
409   }
410 
trimNoArgPlaceholderEndnull411   @Test fun trimNoArgPlaceholderEnd() {
412     assertThat(CodeBlock.of("return ⇥").trim())
413       .isEqualTo(CodeBlock.of("return "))
414   }
415 
trimNoArgPlaceholdersStartEndnull416   @Test fun trimNoArgPlaceholdersStartEnd() {
417     assertThat(CodeBlock.of("«return this»").trim())
418       .isEqualTo(CodeBlock.of("return this"))
419   }
420 
trimMultipleNoArgPlaceholdersnull421   @Test fun trimMultipleNoArgPlaceholders() {
422     assertThat(
423       CodeBlock.of("«return if (x > %L) %S else %S»", 1, "a", "b").trim(),
424     )
425       .isEqualTo(CodeBlock.of("return if (x > %L) %S else %S", 1, "a", "b"))
426   }
427 
trimOnlyNoArgPlaceholdersnull428   @Test fun trimOnlyNoArgPlaceholders() {
429     assertThat(CodeBlock.of("«»⇥⇤").trim())
430       .isEqualTo(CodeBlock.of(""))
431   }
432 
replaceSimplenull433   @Test fun replaceSimple() {
434     assertThat(CodeBlock.of("%%⇥%%").replaceAll("%%", ""))
435       .isEqualTo(CodeBlock.of("⇥"))
436   }
437 
replaceNoMatchesnull438   @Test fun replaceNoMatches() {
439     assertThat(CodeBlock.of("%%⇥%%").replaceAll("⇤", ""))
440       .isEqualTo(CodeBlock.of("%%⇥%%"))
441   }
442 
replaceRegexnull443   @Test fun replaceRegex() {
444     assertThat(CodeBlock.of("%%⇥%%⇤").replaceAll("[⇥|⇤]", ""))
445       .isEqualTo(CodeBlock.of("%%%%"))
446   }
447 
joinToCodenull448   @Test fun joinToCode() {
449     val blocks = listOf(CodeBlock.of("%L", "taco1"), CodeBlock.of("%L", "taco2"), CodeBlock.of("%L", "taco3"))
450     assertThat(blocks.joinToCode(prefix = "(", suffix = ")"))
451       .isEqualTo(CodeBlock.of("(%L, %L, %L)", "taco1", "taco2", "taco3"))
452   }
453 
beginControlFlowWithParamsnull454   @Test fun beginControlFlowWithParams() {
455     val controlFlow = CodeBlock.builder()
456       .beginControlFlow("list.forEach { element ->")
457       .addStatement("println(element)")
458       .endControlFlow()
459       .build()
460     assertThat(controlFlow.toString()).isEqualTo(
461       """
462       |list.forEach { element ->
463       |  println(element)
464       |}
465       |
466       """.trimMargin(),
467     )
468   }
469 
beginControlFlowWithParamsAndTemplateStringnull470   @Test fun beginControlFlowWithParamsAndTemplateString() {
471     val controlFlow = CodeBlock.builder()
472       .beginControlFlow("listOf(\"\${1.toString()}\").forEach { element ->")
473       .addStatement("println(element)")
474       .endControlFlow()
475       .build()
476     assertThat(controlFlow.toString()).isEqualTo(
477       """
478       |listOf("${'$'}{1.toString()}").forEach { element ->
479       |  println(element)
480       |}
481       |
482       """.trimMargin(),
483     )
484   }
485 
buildCodeBlocknull486   @Test fun buildCodeBlock() {
487     val codeBlock = buildCodeBlock {
488       beginControlFlow("if (2 == 2)")
489       addStatement("println(%S)", "foo")
490       nextControlFlow("else")
491       addStatement("println(%S)", "bar")
492       endControlFlow()
493     }
494     assertThat(codeBlock.toString()).isEqualTo(
495       """
496       |if (2 == 2) {
497       |  println("foo")
498       |} else {
499       |  println("bar")
500       |}
501       |
502       """.trimMargin(),
503     )
504   }
505 
nonWrappingControlFlownull506   @Test fun nonWrappingControlFlow() {
507     val file = FileSpec.builder("com.squareup.tacos", "Test")
508       .addFunction(
509         FunSpec.builder("test")
510           .beginControlFlow("if (%1S == %1S)", "Very long string that would wrap the line ")
511           .nextControlFlow("else if (%1S == %1S)", "Long string that would wrap the line 2 ")
512           .endControlFlow()
513           .build(),
514       )
515       .build()
516     assertThat(file.toString()).isEqualTo(
517       """
518       |package com.squareup.tacos
519       |
520       |import kotlin.Unit
521       |
522       |public fun test(): Unit {
523       |  if ("Very long string that would wrap the line " ==
524       |      "Very long string that would wrap the line ") {
525       |  } else if ("Long string that would wrap the line 2 " ==
526       |      "Long string that would wrap the line 2 ") {
527       |  }
528       |}
529       |
530       """.trimMargin(),
531     )
532   }
533 
ensureEndsWithNewLineWithNoArgsnull534   @Test fun ensureEndsWithNewLineWithNoArgs() {
535     val codeBlock = CodeBlock.builder()
536       .addStatement("Modeling a kdoc")
537       .add("\n")
538       .addStatement("Statement with no args")
539       .build()
540 
541     assertThat(codeBlock.ensureEndsWithNewLine().toString()).isEqualTo(
542       """
543       |Modeling a kdoc
544       |
545       |Statement with no args
546       |
547       """.trimMargin(),
548     )
549   }
550 
%N escapes keywordsnull551   @Test fun `%N escapes keywords`() {
552     val funSpec = FunSpec.builder("object").build()
553     assertThat(CodeBlock.of("%N", funSpec).toString()).isEqualTo("`object`")
554   }
555 
%N escapes spacesnull556   @Test fun `%N escapes spaces`() {
557     val funSpec = FunSpec.builder("create taco").build()
558     assertThat(CodeBlock.of("%N", funSpec).toString()).isEqualTo("`create taco`")
559   }
560 
clearnull561   @Test fun clear() {
562     val blockBuilder = CodeBlock.builder().addStatement("%S is some existing code", "This")
563 
564     blockBuilder.clear()
565 
566     assertThat(blockBuilder.build().toString()).isEmpty()
567   }
568 
withIndentnull569   @Test fun withIndent() {
570     val codeBlock = CodeBlock.Builder()
571       .apply {
572         addStatement("User(")
573         withIndent {
574           addStatement("age = 42,")
575           addStatement("cities = listOf(")
576           withIndent {
577             addStatement("%S,", "Berlin")
578             addStatement("%S,", "London")
579           }
580           addStatement(")")
581         }
582         addStatement(")")
583       }
584       .build()
585 
586     assertThat(codeBlock.toString()).isEqualTo(
587       """
588       |User(
589       |  age = 42,
590       |  cities = listOf(
591       |    "Berlin",
592       |    "London",
593       |  )
594       |)
595       |
596       """.trimMargin(),
597     )
598   }
599 
600   // https://github.com/square/kotlinpoet/issues/1236
dontEscapeBackslashesInRawStringsnull601   @Test fun dontEscapeBackslashesInRawStrings() {
602     // println("ESCAPE '\\'") -> ESCAPE '\'
603     assertThat(CodeBlock.of("%S", "ESCAPE '\\'").toString()).isEqualTo("\"ESCAPE '\\\\'\"")
604     // println("""ESCAPE '\'""") -> ESCAPE '\'
605     assertThat(CodeBlock.of("%P", """ESCAPE '\'""").toString()).isEqualTo("\"\"\"ESCAPE '\\'\"\"\"")
606   }
607 
608   // https://github.com/square/kotlinpoet/issues/1381
useUnderscoresOnLargeDecimalLiteralsnull609   @Test fun useUnderscoresOnLargeDecimalLiterals() {
610     assertThat(CodeBlock.of("%L", 10000).toString()).isEqualTo("10_000")
611     assertThat(CodeBlock.of("%L", 100000L).toString()).isEqualTo("100_000")
612     assertThat(CodeBlock.of("%L", Int.MIN_VALUE).toString()).isEqualTo("-2_147_483_648")
613     assertThat(CodeBlock.of("%L", Int.MAX_VALUE).toString()).isEqualTo("2_147_483_647")
614     assertThat(CodeBlock.of("%L", Long.MIN_VALUE).toString()).isEqualTo("-9_223_372_036_854_775_808")
615     assertThat(CodeBlock.of("%L", 10000.123).toString()).isEqualTo("10_000.123")
616     assertThat(CodeBlock.of("%L", 3.0).toString()).isEqualTo("3.0")
617     assertThat(CodeBlock.of("%L", 10000.123f).toString()).isEqualTo("10_000.123")
618     assertThat(CodeBlock.of("%L", 10000.123456789012).toString()).isEqualTo("10_000.123456789011")
619     assertThat(CodeBlock.of("%L", 1281.toShort()).toString()).isEqualTo("1_281")
620 
621     assertThat(CodeBlock.of("%S", 10000).toString()).isEqualTo("\"10000\"")
622     assertThat(CodeBlock.of("%S", 100000L).toString()).isEqualTo("\"100000\"")
623     assertThat(CodeBlock.of("%S", Int.MIN_VALUE).toString()).isEqualTo("\"-2147483648\"")
624     assertThat(CodeBlock.of("%S", Int.MAX_VALUE).toString()).isEqualTo("\"2147483647\"")
625     assertThat(CodeBlock.of("%S", Long.MIN_VALUE).toString()).isEqualTo("\"-9223372036854775808\"")
626     assertThat(CodeBlock.of("%S", 10000.123).toString()).isEqualTo("\"10000.123\"")
627     assertThat(CodeBlock.of("%S", 3.0).toString()).isEqualTo("\"3.0\"")
628     assertThat(CodeBlock.of("%S", 10000.123f).toString()).isEqualTo("\"10000.123\"")
629     assertThat(CodeBlock.of("%S", 10000.12345678901).toString()).isEqualTo("\"10000.12345678901\"")
630     assertThat(CodeBlock.of("%S", 1281.toShort()).toString()).isEqualTo("\"1281\"")
631   }
632 }
633