/* * Copyright (C) 2015 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.javapoet; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.junit.Test; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public final class CodeBlockTest { @Test public void equalsAndHashCode() { CodeBlock a = CodeBlock.builder().build(); CodeBlock b = CodeBlock.builder().build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); a = CodeBlock.builder().add("$L", "taco").build(); b = CodeBlock.builder().add("$L", "taco").build(); assertThat(a.equals(b)).isTrue(); assertThat(a.hashCode()).isEqualTo(b.hashCode()); } @Test public void of() { CodeBlock a = CodeBlock.of("$L taco", "delicious"); assertThat(a.toString()).isEqualTo("delicious taco"); } @Test public void isEmpty() { assertTrue(CodeBlock.builder().isEmpty()); assertTrue(CodeBlock.builder().add("").isEmpty()); assertFalse(CodeBlock.builder().add(" ").isEmpty()); } @Test public void indentCannotBeIndexed() { try { CodeBlock.builder().add("$1>", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void deindentCannotBeIndexed() { try { CodeBlock.builder().add("$1<", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void dollarSignEscapeCannotBeIndexed() { try { CodeBlock.builder().add("$1$", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void statementBeginningCannotBeIndexed() { try { CodeBlock.builder().add("$1[", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void statementEndingCannotBeIndexed() { try { CodeBlock.builder().add("$1]", "taco").build(); fail(); } catch (IllegalArgumentException exp) { assertThat(exp) .hasMessageThat() .isEqualTo("$$, $>, $<, $[, $], $W, and $Z may not have an index"); } } @Test public void nameFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1N", "taco").build(); assertThat(block.toString()).isEqualTo("taco"); } @Test public void literalFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1L", "taco").build(); assertThat(block.toString()).isEqualTo("taco"); } @Test public void stringFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1S", "taco").build(); assertThat(block.toString()).isEqualTo("\"taco\""); } @Test public void typeFormatCanBeIndexed() { CodeBlock block = CodeBlock.builder().add("$1T", String.class).build(); assertThat(block.toString()).isEqualTo("java.lang.String"); } @Test public void simpleNamedArgument() { Map map = new LinkedHashMap<>(); map.put("text", "taco"); CodeBlock block = CodeBlock.builder().addNamed("$text:S", map).build(); assertThat(block.toString()).isEqualTo("\"taco\""); } @Test public void repeatedNamedArgument() { Map map = new LinkedHashMap<>(); map.put("text", "tacos"); CodeBlock block = CodeBlock.builder() .addNamed("\"I like \" + $text:S + \". Do you like \" + $text:S + \"?\"", map) .build(); assertThat(block.toString()).isEqualTo( "\"I like \" + \"tacos\" + \". Do you like \" + \"tacos\" + \"?\""); } @Test public void namedAndNoArgFormat() { Map map = new LinkedHashMap<>(); map.put("text", "tacos"); CodeBlock block = CodeBlock.builder() .addNamed("$>\n$text:L for $$3.50", map).build(); assertThat(block.toString()).isEqualTo("\n tacos for $3.50"); } @Test public void missingNamedArgument() { try { Map map = new LinkedHashMap<>(); CodeBlock.builder().addNamed("$text:S", map).build(); fail(); } catch(IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("Missing named argument for $text"); } } @Test public void lowerCaseNamed() { try { Map map = new LinkedHashMap<>(); map.put("Text", "tacos"); CodeBlock block = CodeBlock.builder().addNamed("$Text:S", map).build(); fail(); } catch(IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("argument 'Text' must start with a lowercase character"); } } @Test public void multipleNamedArguments() { Map map = new LinkedHashMap<>(); map.put("pipe", System.class); map.put("text", "tacos"); CodeBlock block = CodeBlock.builder() .addNamed("$pipe:T.out.println(\"Let's eat some $text:L\");", map) .build(); assertThat(block.toString()).isEqualTo( "java.lang.System.out.println(\"Let's eat some tacos\");"); } @Test public void namedNewline() { Map map = new LinkedHashMap<>(); map.put("clazz", Integer.class); CodeBlock block = CodeBlock.builder().addNamed("$clazz:T\n", map).build(); assertThat(block.toString()).isEqualTo("java.lang.Integer\n"); } @Test public void danglingNamed() { Map map = new LinkedHashMap<>(); map.put("clazz", Integer.class); try { CodeBlock.builder().addNamed("$clazz:T$", map).build(); fail(); } catch(IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("dangling $ at end"); } } @Test public void indexTooHigh() { try { CodeBlock.builder().add("$2T", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("index 2 for '$2T' not in range (received 1 arguments)"); } } @Test public void indexIsZero() { try { CodeBlock.builder().add("$0T", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("index 0 for '$0T' not in range (received 1 arguments)"); } } @Test public void indexIsNegative() { try { CodeBlock.builder().add("$-1T", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("invalid format string: '$-1T'"); } } @Test public void indexWithoutFormatType() { try { CodeBlock.builder().add("$1", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("dangling format characters in '$1'"); } } @Test public void indexWithoutFormatTypeNotAtStringEnd() { try { CodeBlock.builder().add("$1 taco", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("invalid format string: '$1 taco'"); } } @Test public void indexButNoArguments() { try { CodeBlock.builder().add("$1T").build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("index 1 for '$1T' not in range (received 0 arguments)"); } } @Test public void formatIndicatorAlone() { try { CodeBlock.builder().add("$", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("dangling format characters in '$'"); } } @Test public void formatIndicatorWithoutIndexOrFormatType() { try { CodeBlock.builder().add("$ tacoString", String.class).build(); fail(); } catch (IllegalArgumentException expected) { assertThat(expected).hasMessageThat().isEqualTo("invalid format string: '$ tacoString'"); } } @Test public void sameIndexCanBeUsedWithDifferentFormats() { CodeBlock block = CodeBlock.builder() .add("$1T.out.println($1S)", ClassName.get(System.class)) .build(); assertThat(block.toString()).isEqualTo("java.lang.System.out.println(\"java.lang.System\")"); } @Test public void tooManyStatementEnters() { CodeBlock codeBlock = CodeBlock.builder().add("$[$[").build(); try { // We can't report this error until rendering type because code blocks might be composed. codeBlock.toString(); fail(); } catch (IllegalStateException expected) { assertThat(expected).hasMessageThat().isEqualTo("statement enter $[ followed by statement enter $["); } } @Test public void statementExitWithoutStatementEnter() { CodeBlock codeBlock = CodeBlock.builder().add("$]").build(); try { // We can't report this error until rendering type because code blocks might be composed. codeBlock.toString(); fail(); } catch (IllegalStateException expected) { assertThat(expected).hasMessageThat().isEqualTo("statement exit $] has no matching statement enter $["); } } @Test public void join() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); codeBlocks.add(CodeBlock.of("$T", ClassName.get("world", "World"))); codeBlocks.add(CodeBlock.of("need tacos")); CodeBlock joined = CodeBlock.join(codeBlocks, " || "); assertThat(joined.toString()).isEqualTo("\"hello\" || world.World || need tacos"); } @Test public void joining() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); codeBlocks.add(CodeBlock.of("$T", ClassName.get("world", "World"))); codeBlocks.add(CodeBlock.of("need tacos")); CodeBlock joined = codeBlocks.stream().collect(CodeBlock.joining(" || ")); assertThat(joined.toString()).isEqualTo("\"hello\" || world.World || need tacos"); } @Test public void joiningSingle() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); CodeBlock joined = codeBlocks.stream().collect(CodeBlock.joining(" || ")); assertThat(joined.toString()).isEqualTo("\"hello\""); } @Test public void joiningWithPrefixAndSuffix() { List codeBlocks = new ArrayList<>(); codeBlocks.add(CodeBlock.of("$S", "hello")); codeBlocks.add(CodeBlock.of("$T", ClassName.get("world", "World"))); codeBlocks.add(CodeBlock.of("need tacos")); CodeBlock joined = codeBlocks.stream().collect(CodeBlock.joining(" || ", "start {", "} end")); assertThat(joined.toString()).isEqualTo("start {\"hello\" || world.World || need tacos} end"); } }