1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.googlejavaformat.java;
16 
17 import static com.google.common.truth.Truth.assertThat;
18 import static java.nio.charset.StandardCharsets.UTF_8;
19 import static org.junit.Assert.assertEquals;
20 
21 import com.google.common.base.Joiner;
22 import com.google.common.collect.ImmutableList;
23 import com.google.common.collect.ObjectArrays;
24 import com.google.common.collect.Range;
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.io.StringWriter;
28 import java.nio.file.Files;
29 import java.nio.file.Path;
30 import java.util.ArrayList;
31 import java.util.List;
32 import org.junit.Rule;
33 import org.junit.Test;
34 import org.junit.rules.TemporaryFolder;
35 import org.junit.runner.RunWith;
36 import org.junit.runners.Parameterized;
37 import org.junit.runners.Parameterized.Parameters;
38 
39 /** Tests formatting parts of files. */
40 @RunWith(Parameterized.class)
41 public final class PartialFormattingTest {
42 
43   @Parameters
parameters()44   public static Iterable<Object[]> parameters() {
45     return ImmutableList.copyOf(new Object[][] {{"\n"}, {"\r"}, {"\r\n"}});
46   }
47 
48   @Rule public TemporaryFolder testFolder = new TemporaryFolder();
49 
50   private final String newline;
51 
PartialFormattingTest(String newline)52   public PartialFormattingTest(String newline) {
53     this.newline = newline;
54   }
55 
lines(String... args)56   String lines(String... args) {
57     return Joiner.on(newline).join(args);
58   }
59 
60   @Test
testGetFormatReplacements0()61   public void testGetFormatReplacements0() throws Exception {
62     String input =
63         lines(
64             /* line 0 character  0 */ "class Foo{",
65             /* line 1 character 11 */ "void f",
66             /* line 2 character 18 */ "() {",
67             /* line 3 character 23 */ "}",
68             /* line 4 character 25 */ "}",
69             "");
70     String expectedOutput =
71         lines(
72 
73             /* line 0 character  0 */ "class Foo{",
74             /* line 1 character 11 */ "  void f() {}",
75             /* line 2 character 25 */ "}",
76             "");
77     // Claim to have modified the parentheses.
78     int start = input.indexOf("() {");
79     String output = doGetFormatReplacements(input, start, start + 1);
80     assertThat(output).isEqualTo(expectedOutput);
81   }
82 
83   @Test
testGetFormatReplacements1()84   public void testGetFormatReplacements1() throws Exception {
85     String input =
86         lines(
87             /* line 0 character  0 */ "class Foo{",
88             /* line 1 character 11 */ "void f",
89             /* line 2 character 18 */ "() {",
90             /* line 3 character 23 */ "}",
91             /* line 4 character 25 */ "}",
92             "");
93     String expectedOutput =
94         lines(
95             /* line 0 character  0 */ "class Foo{",
96             /* line 1 character 11 */ "  void f() {}",
97             /* line 2 character 25 */ "}",
98             "");
99     // Claim to have modified everything after the parentheses.
100     String output = doGetFormatReplacements(input, 20, 21);
101     assertThat(output).isEqualTo(expectedOutput);
102   }
103 
104   @Test
expandToStatement()105   public void expandToStatement() throws Exception {
106     String input =
107         lines(
108             "class Foo {{",
109             "ImmutableList<Integer> ids = ImmutableList.builder()",
110             ".add(1)",
111             ".add(2)",
112             ".add(3)",
113             ".build();",
114             "}}",
115             "");
116     String expectedOutput =
117         lines(
118             "class Foo {{",
119             "    ImmutableList<Integer> ids ="
120                 + " ImmutableList.builder().add(1).add(2).add(3).build();",
121             "}}",
122             "");
123     int idx = input.indexOf("add(2)");
124     String output = doGetFormatReplacements(input, idx, idx + 1);
125     assertThat(output).isEqualTo(expectedOutput);
126   }
127 
128   @Test
expandToMethodSignature()129   public void expandToMethodSignature() throws Exception {
130     String input =
131         lines(
132             "class Foo {",
133             "void ",
134             " m() ",
135             " {",
136             "ImmutableList<Integer> ids = ImmutableList.builder()",
137             ".add(1)",
138             ".add(2)",
139             ".build();",
140             "}}",
141             "");
142     String expectedOutput =
143         lines(
144             "class Foo {",
145             "  void m() {",
146             "ImmutableList<Integer> ids = ImmutableList.builder()",
147             ".add(1)",
148             ".add(2)",
149             ".build();",
150             "}}",
151             "");
152     int idx = input.indexOf("void");
153     String output = doGetFormatReplacements(input, idx, idx + 1);
154     assertThat(output).isEqualTo(expectedOutput);
155   }
156 
157   @Test
expandToClassSignature()158   public void expandToClassSignature() throws Exception {
159     String input =
160         lines(
161             "class",
162             "Foo<XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,"
163                 + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,"
164                 + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX> {",
165             "void ",
166             " m() ",
167             " {",
168             "}}",
169             "");
170     String expectedOutput =
171         lines(
172             "class Foo<",
173             "    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,",
174             "    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,",
175             "    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX> {",
176             "void ",
177             " m() ",
178             " {",
179             "}}",
180             "");
181     int idx = input.indexOf("class");
182     String output = doGetFormatReplacements(input, idx, idx + 1);
183     assertThat(output).isEqualTo(expectedOutput);
184   }
185 
186   @Test
ignoreNeighbouringStatements()187   public void ignoreNeighbouringStatements() throws Exception {
188     String input =
189         lines(
190             "class Test {",
191             "int ",
192             " xxx ",
193             " = 1;",
194             "int ",
195             " yyy ",
196             " = 1;",
197             "int ",
198             " zzz ",
199             " = 1;",
200             "}",
201             "");
202     String expectedOutput =
203         lines(
204             "class Test {",
205             "int ",
206             " xxx ",
207             " = 1;",
208             "  int yyy = 1;",
209             "int ",
210             " zzz ",
211             " = 1;",
212             "}",
213             "");
214     int idx = input.indexOf("yyy");
215     String output = doGetFormatReplacements(input, idx, idx + 1);
216     assertThat(output).isEqualTo(expectedOutput);
217   }
218 
219   @Test
insertLeadingNewlines()220   public void insertLeadingNewlines() throws Exception {
221     String input =
222         lines(
223             "class Test { int xxx = 1; int yyy = 1; int zzz = 1; }", //
224             "");
225     String expectedOutput =
226         lines(
227             "class Test { int xxx = 1;", //
228             "  int yyy = 1;",
229             "  int zzz = 1; }",
230             "");
231     int idx = input.indexOf("yyy");
232     String output = doGetFormatReplacements(input, idx, idx + 1);
233     assertThat(output).isEqualTo(expectedOutput);
234   }
235 
236   @Test
insertLeadingNewlines2()237   public void insertLeadingNewlines2() throws Exception {
238     String input =
239         lines(
240             "class Test { int xxx = 1;", //
241             "",
242             "         int yyy = 1; int zzz = 1; }");
243     String expectedOutput =
244         lines(
245             "class Test { int xxx = 1;", //
246             "", //
247             "  int yyy = 1;",
248             "  int zzz = 1; }");
249     int idx = input.indexOf("yyy");
250     String output = doGetFormatReplacements(input, idx, idx + 1);
251     assertThat(output).isEqualTo(expectedOutput);
252   }
253 
254   @Test
insertTrailingNewlines()255   public void insertTrailingNewlines() throws Exception {
256     String input =
257         lines(
258             "class Test { int xxx = 1;", //
259             "  int yyy = 1;      int zzz = 1; }");
260     String expectedOutput =
261         lines(
262             "class Test { int xxx = 1;", //
263             "  int yyy = 1;",
264             "  int zzz = 1; }");
265     int idx = input.indexOf("yyy");
266     String output = doGetFormatReplacements(input, idx, idx + 1);
267     assertThat(output).isEqualTo(expectedOutput);
268   }
269 
270   @Test
rejoinMethodSignatureLines()271   public void rejoinMethodSignatureLines() throws Exception {
272     String input =
273         lines(
274             "class Test { void zzz", //
275             "() { int x; } }");
276     String expectedOutput =
277         lines(
278             "class Test {", //
279             "  void zzz() {",
280             "    int x; } }");
281     int idx = input.indexOf("zzz");
282     String output = doGetFormatReplacements(input, idx, idx);
283     assertThat(output).isEqualTo(expectedOutput);
284   }
285 
286   @Test
formatTrailingBrace()287   public void formatTrailingBrace() throws Exception {
288     String input =
289         lines(
290             "class Test { void f() { return; } }", //
291             "");
292     String expectedOutput =
293         lines(
294             "class Test { void f() { return;", //
295             "  }",
296             "}",
297             "");
298     int idx = input.indexOf("}");
299     String output = doGetFormatReplacements(input, idx, idx);
300     assertThat(output).isEqualTo(expectedOutput);
301   }
302 
303   @Test
formatTrailingBraceEmptyMethodBody()304   public void formatTrailingBraceEmptyMethodBody() throws Exception {
305     String input =
306         lines(
307             "class Test { void f() {} }", //
308             "");
309     String expectedOutput =
310         lines(
311             "class Test {", //
312             "  void f() {}",
313             "}",
314             "");
315     int idx = input.indexOf("}");
316     String output = doGetFormatReplacements(input, idx, idx);
317     assertThat(output).isEqualTo(expectedOutput);
318   }
319 
320   @Test
formatTrailingBraceEmptyClassBody()321   public void formatTrailingBraceEmptyClassBody() throws Exception {
322     String input =
323         lines(
324             "class Test { int x; }", //
325             "");
326     String expectedOutput =
327         lines(
328             "class Test { int x;", //
329             "}",
330             "");
331     int idx = input.indexOf("}");
332     String output = doGetFormatReplacements(input, idx, idx);
333     assertThat(output).isEqualTo(expectedOutput);
334   }
335 
336   @Test
formatTrailingBraceEmptyClassBody2()337   public void formatTrailingBraceEmptyClassBody2() throws Exception {
338     String input =
339         lines(
340             "class Test {", //
341             "}",
342             "");
343     String expectedOutput =
344         lines(
345             "class Test {}", //
346             "");
347     int idx = input.indexOf("}");
348     String output = doGetFormatReplacements(input, idx, idx);
349     assertThat(output).isEqualTo(expectedOutput);
350   }
351 
352   @Test
onlyPackage()353   public void onlyPackage() throws Exception {
354     String input =
355         lines(
356             "package", //
357             "test",
358             ";",
359             "class Test {}",
360             "");
361     String expectedOutput =
362         lines(
363             "package test;", //
364             "",
365             "class Test {}",
366             "");
367     int idx = input.indexOf("test");
368     String output = doGetFormatReplacements(input, idx, idx);
369     assertThat(output).isEqualTo(expectedOutput);
370   }
371 
doGetFormatReplacements(String input, int characterILo, int characterIHi)372   private static String doGetFormatReplacements(String input, int characterILo, int characterIHi)
373       throws Exception {
374     return new Formatter()
375         .formatSource(input, ImmutableList.of(Range.closedOpen(characterILo, characterIHi + 1)));
376   }
377 
378   @Test
testLength()379   public void testLength() throws Exception {
380     String input =
381         lines(
382             "class Foo{", //
383             "int xxx;",
384             "int yyy;",
385             "int zzz;",
386             "}",
387             "");
388     String expectedOutput =
389         lines(
390             "class Foo{", //
391             "int xxx;",
392             "  int yyy;",
393             "int zzz;",
394             "}",
395             "");
396 
397     Path tmpdir = testFolder.newFolder().toPath();
398     Path path = tmpdir.resolve("Foo.java");
399     Files.write(path, input.getBytes(UTF_8));
400 
401     StringWriter out = new StringWriter();
402     StringWriter err = new StringWriter();
403 
404     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
405     String[] args = {"-lines", "3", path.toString()};
406     assertThat(main.format(args)).isEqualTo(0);
407     assertThat(out.toString()).isEqualTo(expectedOutput);
408   }
409 
410   @Test
testLengthRange()411   public void testLengthRange() throws Exception {
412     String input =
413         lines(
414             "class Foo{", //
415             "int xxx;",
416             "int yyy;",
417             "int zzz;",
418             "}",
419             "");
420     String expectedOutput =
421         lines(
422             "class Foo{", //
423             "int xxx;",
424             "  int yyy;",
425             "  int zzz;",
426             "}",
427             "");
428 
429     Path tmpdir = testFolder.newFolder().toPath();
430     Path path = tmpdir.resolve("Foo.java");
431     Files.write(path, input.getBytes(UTF_8));
432 
433     StringWriter out = new StringWriter();
434     StringWriter err = new StringWriter();
435 
436     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
437     String[] args = {"-lines", "3:4", path.toString()};
438     assertThat(main.format(args)).isEqualTo(0);
439     assertThat(out.toString()).isEqualTo(expectedOutput);
440   }
441 
442   @Test
statementAndComments()443   public void statementAndComments() throws Exception {
444     String input =
445         lines(
446             "public class MyTest {",
447             "{",
448             "// asd",
449             "int x = 1;",
450             "// asd",
451             "int y = 2;",
452             "// asd",
453             "int z = 3;",
454             "// asd",
455             "}",
456             "}",
457             "",
458             "");
459     String expectedOutput =
460         lines(
461             "public class MyTest {",
462             "{",
463             "// asd",
464             "int x = 1;",
465             "    // asd",
466             "    int y = 2;",
467             "// asd",
468             "int z = 3;",
469             "// asd",
470             "}",
471             "}",
472             "",
473             "");
474 
475     Path tmpdir = testFolder.newFolder().toPath();
476     Path path = tmpdir.resolve("Foo.java");
477     Files.write(path, input.getBytes(UTF_8));
478 
479     StringWriter out = new StringWriter();
480     StringWriter err = new StringWriter();
481 
482     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
483     String[] args = {"-lines", "5", path.toString()};
484     assertThat(main.format(args)).isEqualTo(0);
485     assertThat(out.toString()).isEqualTo(expectedOutput);
486   }
487 
488   @Test
statementAndComments2()489   public void statementAndComments2() throws Exception {
490     String input =
491         lines(
492             "public class MyTest {",
493             "{",
494             "// asd",
495             "int x = 1;",
496             "// asd",
497             "int y = 2;",
498             "// asd",
499             "int z = 3;",
500             "// asd",
501             "}",
502             "}",
503             "",
504             "");
505     String expectedOutput =
506         lines(
507             "public class MyTest {",
508             "{",
509             "// asd",
510             "int x = 1;",
511             "    // asd",
512             "    int y = 2;",
513             "// asd",
514             "int z = 3;",
515             "// asd",
516             "}",
517             "}",
518             "",
519             "");
520 
521     Path tmpdir = testFolder.newFolder().toPath();
522     Path path = tmpdir.resolve("Foo.java");
523     Files.write(path, input.getBytes(UTF_8));
524 
525     StringWriter out = new StringWriter();
526     StringWriter err = new StringWriter();
527 
528     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
529     String[] args = {"-lines", "6", path.toString()};
530     assertThat(main.format(args)).isEqualTo(0);
531     assertThat(out.toString()).isEqualTo(expectedOutput);
532   }
533 
534   @Test
statementAndComments3()535   public void statementAndComments3() throws Exception {
536     String input =
537         lines(
538             "public class MyTest {",
539             "{",
540             "// asd",
541             "int x = 1;",
542             "// asd",
543             "int y = 2;",
544             "// asd",
545             "int z = 3;",
546             "// asd",
547             "}",
548             "}",
549             "",
550             "");
551     String expectedOutput =
552         lines(
553             "public class MyTest {",
554             "{",
555             "// asd",
556             "int x = 1;",
557             "// asd",
558             "int y = 2;",
559             "    // asd",
560             "    int z = 3;",
561             "// asd",
562             "}",
563             "}",
564             "",
565             "");
566 
567     Path tmpdir = testFolder.newFolder().toPath();
568     Path path = tmpdir.resolve("Foo.java");
569     Files.write(path, input.getBytes(UTF_8));
570 
571     StringWriter out = new StringWriter();
572     StringWriter err = new StringWriter();
573 
574     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
575     String[] args = {"-lines", "7", path.toString()};
576     assertThat(main.format(args)).isEqualTo(0);
577     assertThat(out.toString()).isEqualTo(expectedOutput);
578   }
579 
580   @Test
blankAndComment()581   public void blankAndComment() throws Exception {
582     String input =
583         lines(
584             "public class MyTest {",
585             "  public void testListDefinitions() throws Exception {",
586             "    definitionService.insert(createDefinition(1));",
587             "    definitionService.insert(createIncrementalDefinition(2));",
588             "    definitionService.insert(createDefinition(3));",
589             "    definitionService.insert(createIncrementalDefinition(4));",
590             "",
591             "    // No maxResults",
592             "    assertThat(achievementFirstPartyHelper.listDefinitionsByApplication(",
593             "            STUB_GAIA_ID, STUB_APPLICATION_ID, Optional.<Integer>absent(),",
594             "           "
595                 + " Optional.<String>absent()).getAchievements()).containsExactly(createExpectedDefinition(1),"
596                 + " createIncrementalExpectedDefinition(2), createExpectedDefinition(3),"
597                 + " createIncrementalExpectedDefinition(4)).inOrder();",
598             "  }",
599             "}",
600             "",
601             "");
602     String expectedOutput =
603         lines(
604             "public class MyTest {",
605             "  public void testListDefinitions() throws Exception {",
606             "    definitionService.insert(createDefinition(1));",
607             "    definitionService.insert(createIncrementalDefinition(2));",
608             "    definitionService.insert(createDefinition(3));",
609             "    definitionService.insert(createIncrementalDefinition(4));",
610             "",
611             "    // No maxResults",
612             "    assertThat(",
613             "            achievementFirstPartyHelper",
614             "                .listDefinitionsByApplication(",
615             "                    STUB_GAIA_ID,",
616             "                    STUB_APPLICATION_ID,",
617             "                    Optional.<Integer>absent(),",
618             "                    Optional.<String>absent())",
619             "                .getAchievements())",
620             "        .containsExactly(",
621             "            createExpectedDefinition(1),",
622             "            createIncrementalExpectedDefinition(2),",
623             "            createExpectedDefinition(3),",
624             "            createIncrementalExpectedDefinition(4))",
625             "        .inOrder();",
626             "  }",
627             "}",
628             "",
629             "");
630 
631     String toFormat =
632         lines(
633             "    assertThat(achievementFirstPartyHelper.listDefinitionsByApplication(", //
634             "");
635     int idx = input.indexOf(toFormat);
636     String output = doGetFormatReplacements(input, idx, idx + toFormat.length());
637     assertThat(output).isEqualTo(expectedOutput);
638   }
639 
640   @Test
emptyFile()641   public void emptyFile() throws Exception {
642     new Formatter().formatSource("");
643     new Formatter()
644         .formatSource(
645             lines(
646                 "", //
647                 ""),
648             ImmutableList.of(Range.closedOpen(0, 1)));
649   }
650 
651   @Test
testGetFormatReplacementRanges()652   public void testGetFormatReplacementRanges() throws Exception {
653     String input =
654         lines(
655             /* line 0 character  0 */ "class Foo{",
656             /* line 1 character 11 */ "void f",
657             /* line 2 character 18 */ "() {",
658             /* line 3 character 23 */ "}",
659             /* line 4 character 25 */ "}",
660             "");
661     // Claim to have modified the parentheses.
662     int start = input.indexOf("() {");
663     ImmutableList<Replacement> ranges =
664         new Formatter()
665             .getFormatReplacements(input, ImmutableList.of(Range.closedOpen(start, start + 1)));
666     assertThat(ranges).hasSize(1);
667     Replacement replacement = ranges.get(0);
668     assertThat(replacement.getReplacementString())
669         .isEqualTo(
670             lines(
671                 "", //
672                 "  void f() {}"));
673     int replaceFrom = input.indexOf("void f") - newline.length();
674     assertThat(replacement.getReplaceRange().lowerEndpoint()).isEqualTo(replaceFrom);
675   }
676 
677   @Test
noTokensOnLine()678   public void noTokensOnLine() throws Exception {
679     String input =
680         lines(
681             "    package com.google.googlejavaformat.java;",
682             "",
683             "/*",
684             " * Copyright 2015 Google Inc.",
685             " *",
686             " * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not",
687             " * in compliance with the License. You may obtain a copy of the License at",
688             " *",
689             " *     http://www.apache.org/licenses/LICENSE-2.0",
690             " *",
691             " * Unless required by applicable law or agreed to in writing, software distribute",
692             " * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY",
693             " * or implied. See the License for the specific language governing permissions an",
694             " * the License.",
695             " */",
696             "",
697             "import com.google.googlejavaformat.FormatterDiagnostic;",
698             "import java.util.List;",
699             "",
700             "/** Checked exception class for formatter errors. */",
701             "public final class FormatterException extends Exception {",
702             "",
703             "  FormatterException(String message) {",
704             "    super(message);",
705             "  }",
706             "",
707             "  /**",
708             "   * @param errors",
709             "   */",
710             "  public FormatterException(List<FormatterDiagnostic> errors) {",
711             "    // TODO(cushon): Auto-generated constructor stub",
712             "  }",
713             "}");
714 
715     Path tmpdir = testFolder.newFolder().toPath();
716     Path path = tmpdir.resolve("FormatterException.java");
717     Files.write(path, input.getBytes(UTF_8));
718 
719     StringWriter out = new StringWriter();
720     StringWriter err = new StringWriter();
721 
722     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
723     String[] args = {"-lines", "3:4", path.toString()};
724     assertThat(main.format(args)).isEqualTo(0);
725     assertThat(out.toString()).isEqualTo(input);
726   }
727 
728   @Test
nestedStatement1()729   public void nestedStatement1() throws Exception {
730     String input =
731         lines(
732             "public class MyTest {{",
733             "int x = ",
734             " 1;",
735             "int y = new Runnable() {",
736             "  void run() {",
737             "    System.err.println(42);",
738             "  }",
739             "};",
740             "int z = ",
741             " 1;",
742             "}}",
743             "",
744             "");
745     String expectedOutput =
746         lines(
747             "public class MyTest {{",
748             "int x = ",
749             " 1;",
750             "    int y =",
751             "        new Runnable() {",
752             "          void run() {",
753             "            System.err.println(42);",
754             "          }",
755             "        };",
756             "int z = ",
757             " 1;",
758             "}}",
759             "",
760             "");
761 
762     String toFormat = "Runnable";
763     int idx = input.indexOf(toFormat);
764     String output = doGetFormatReplacements(input, idx, idx + toFormat.length());
765     assertThat(output).isEqualTo(expectedOutput);
766   }
767 
768   @Test
nestedStatement2()769   public void nestedStatement2() throws Exception {
770     String input =
771         lines(
772             "public class MyTest {",
773             "int x = ",
774             " 1;",
775             "int y = new Runnable() {",
776             "  void run() {",
777             "    System.err.println(42);",
778             "  }",
779             "};",
780             "int z = ",
781             " 1;",
782             "}",
783             "",
784             "");
785     String expectedOutput =
786         lines(
787             "public class MyTest {",
788             "int x = ",
789             " 1;",
790             "  int y =",
791             "      new Runnable() {",
792             "        void run() {",
793             "          System.err.println(42);",
794             "        }",
795             "      };",
796             "int z = ",
797             " 1;",
798             "}",
799             "",
800             "");
801 
802     String toFormat = "Runnable";
803     int idx = input.indexOf(toFormat);
804     String output = doGetFormatReplacements(input, idx, idx + toFormat.length());
805     assertThat(output).isEqualTo(expectedOutput);
806   }
807 
808   @Test
blankLine()809   public void blankLine() throws Exception {
810     String input =
811         lines(
812             "public class MyTest {", //
813             "int x = 1;",
814             "",
815             "int y = 1;",
816             "}",
817             "",
818             "");
819     String expectedOutput = input;
820 
821     testFormatLine(input, expectedOutput, 3);
822   }
823 
824   @Test
lineWithIdentifier()825   public void lineWithIdentifier() throws Exception {
826     String input =
827         lines(
828             "public class MyTest {", //
829             "int",
830             "y",
831             "= 1;",
832             "}",
833             "",
834             "");
835     String expectedOutput =
836         lines(
837             "public class MyTest {", //
838             "  int y = 1;",
839             "}",
840             "",
841             "");
842 
843     testFormatLine(input, expectedOutput, 3);
844   }
845 
846   // formatted region expands to include entire comment
847   @Test
lineInsideComment()848   public void lineInsideComment() throws Exception {
849     String input =
850         lines(
851             "public class MyTest {",
852             "/* This is a",
853             "            poorly indented",
854             "                       comment*/",
855             "int x;",
856             "}",
857             "",
858             "");
859     String expectedOutput =
860         lines(
861             "public class MyTest {",
862             "  /* This is a",
863             "  poorly indented",
864             "             comment*/",
865             "  int x;",
866             "}",
867             "",
868             "");
869 
870     testFormatLine(input, expectedOutput, 3);
871   }
872 
873   @Test
testReplacementsSorted()874   public void testReplacementsSorted() throws Exception {
875     String input =
876         lines(
877             "class Test {",
878             "int a = 1;",
879             "int b = 2;",
880             "int c = 3;",
881             "int d = 4;",
882             "int e = 5;",
883             "}");
884     List<Range<Integer>> ranges = new ArrayList<>();
885     for (int i = 1; i <= 5; i += 2) {
886       int idx = input.indexOf(String.valueOf(i));
887       ranges.add(Range.closedOpen(idx, idx + 1));
888     }
889 
890     ImmutableList<Replacement> replacements = new Formatter().getFormatReplacements(input, ranges);
891 
892     // expect replacements in ascending order, by start position
893     List<Integer> startPositions = new ArrayList<>();
894     for (Replacement replacement : replacements) {
895       startPositions.add(replacement.getReplaceRange().lowerEndpoint());
896     }
897     assertThat(startPositions).hasSize(3);
898     assertThat(startPositions).isInStrictOrder();
899   }
900 
901   @Test
testReplacementsSorted_DescendingInput()902   public void testReplacementsSorted_DescendingInput() throws Exception {
903     String input =
904         lines(
905             "class Test {",
906             "int a = 1;",
907             "int b = 2;",
908             "int c = 3;",
909             "int d = 4;",
910             "int e = 5;",
911             "}");
912     List<Range<Integer>> ranges = new ArrayList<>();
913     for (int i = 5; i >= 1; i -= 2) {
914       int idx = input.indexOf(String.valueOf(i));
915       ranges.add(Range.closedOpen(idx, idx + 1));
916     }
917 
918     ImmutableList<Replacement> replacements = new Formatter().getFormatReplacements(input, ranges);
919 
920     // expect replacements in ascending order, by start position
921     List<Integer> startPositions = new ArrayList<>();
922     for (Replacement replacement : replacements) {
923       startPositions.add(replacement.getReplaceRange().lowerEndpoint());
924     }
925     assertThat(startPositions).hasSize(3);
926     assertThat(startPositions).isInStrictOrder();
927   }
928 
testFormatLine(String input, String expectedOutput, int i)929   private void testFormatLine(String input, String expectedOutput, int i) throws Exception {
930     Path tmpdir = testFolder.newFolder().toPath();
931     Path path = tmpdir.resolve("Foo.java");
932     Files.write(path, input.getBytes(UTF_8));
933 
934     StringWriter out = new StringWriter();
935     StringWriter err = new StringWriter();
936 
937     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
938     String[] args = {"-lines", Integer.toString(i), path.toString()};
939     assertThat(main.format(args)).isEqualTo(0);
940     assertThat(out.toString()).isEqualTo(expectedOutput);
941   }
942 
943   @Test
lineWithTrailingComment()944   public void lineWithTrailingComment() throws Exception {
945     String input =
946         lines(
947             "class Foo{", //
948             "int xxx; // asd",
949             "}",
950             "");
951     String expectedOutput =
952         lines(
953             "class Foo{", //
954             "  int xxx; // asd",
955             "}",
956             "");
957 
958     Path tmpdir = testFolder.newFolder().toPath();
959     Path path = tmpdir.resolve("Foo.java");
960     Files.write(path, input.getBytes(UTF_8));
961 
962     StringWriter out = new StringWriter();
963     StringWriter err = new StringWriter();
964 
965     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
966     String[] args = {"-lines", "2", path.toString()};
967     assertThat(main.format(args)).isEqualTo(0);
968     assertThat(out.toString()).isEqualTo(expectedOutput);
969   }
970 
971   // Nested statements are OK as long as they're nested inside "block-like" constructs.
972   @Test
nestedStatement_allowPartial()973   public void nestedStatement_allowPartial() throws Exception {
974     String input =
975         lines(
976             "public class MyTest {{",
977             "if (true) {",
978             "if (true) {",
979             "System.err.println(\"Hello\");",
980             "} else {",
981             "System.err.println(\"Goodbye\");",
982             "}",
983             "}",
984             "}}",
985             "");
986     String expectedOutput =
987         lines(
988             "public class MyTest {{",
989             "if (true) {",
990             "if (true) {",
991             "        System.err.println(\"Hello\");",
992             "} else {",
993             "System.err.println(\"Goodbye\");",
994             "}",
995             "}",
996             "}}",
997             "");
998 
999     String toFormat = "Hello";
1000     int idx = input.indexOf(toFormat);
1001     String output = doGetFormatReplacements(input, idx, idx + toFormat.length());
1002     assertThat(output).isEqualTo(expectedOutput);
1003   }
1004 
1005   // regression test for b/b22196513
1006   @Test
noTrailingWhitespace()1007   public void noTrailingWhitespace() throws Exception {
1008     String input =
1009         lines(
1010             "", //
1011             "class Test {",
1012             "  {",
1013             "    {",
1014             "      {",
1015             "      }",
1016             "    }",
1017             "}}",
1018             "");
1019     String expected =
1020         lines(
1021             "", //
1022             "class Test {",
1023             "  {",
1024             "    {",
1025             "      {",
1026             "      }",
1027             "    }",
1028             "  }",
1029             "}",
1030             "");
1031     int start = input.indexOf(newline + "}}");
1032     ImmutableList<Range<Integer>> ranges =
1033         ImmutableList.of(Range.closedOpen(start, start + newline.length() + 2));
1034     String output = new Formatter().formatSource(input, ranges);
1035     assertEquals("bad output", expected, output);
1036   }
1037 
1038   // regression test for b/b22196513
1039   @Test
trailingNonBreakingWhitespace()1040   public void trailingNonBreakingWhitespace() throws Exception {
1041     String input =
1042         lines(
1043             "", //
1044             "class Test {",
1045             "  {",
1046             "    int x;int y;",
1047             "  }",
1048             "}",
1049             "");
1050     String expected =
1051         lines(
1052             "", //
1053             "class Test {",
1054             "  {",
1055             "    int x;",
1056             "    int y;",
1057             "  }",
1058             "}",
1059             "");
1060     String match = "int x;";
1061     int start = input.indexOf(match);
1062     int end = start + match.length();
1063     ImmutableList<Range<Integer>> ranges = ImmutableList.of(Range.closedOpen(start, end));
1064     String output = new Formatter().formatSource(input, ranges);
1065     assertEquals("bad output", expected, output);
1066   }
1067 
1068   @Test
outOfRangeStartLine()1069   public void outOfRangeStartLine() throws Exception {
1070     String input =
1071         lines(
1072             "class Foo {", //
1073             "int x = 1;",
1074             "}");
1075     String expectedOutput =
1076         lines(
1077             "class Foo {", //
1078             "  int x = 1;",
1079             "}",
1080             "");
1081 
1082     Path tmpdir = testFolder.newFolder().toPath();
1083     Path path = tmpdir.resolve("Foo.java");
1084     Files.write(path, input.getBytes(UTF_8));
1085 
1086     StringWriter out = new StringWriter();
1087     StringWriter err = new StringWriter();
1088 
1089     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1090     String[] args = {"-lines", "-1:3", path.toString()};
1091     assertThat(main.format(args)).isEqualTo(0);
1092     assertThat(out.toString()).isEqualTo(expectedOutput);
1093   }
1094 
1095   @Test
outOfRangeEndLine()1096   public void outOfRangeEndLine() throws Exception {
1097     String input =
1098         lines(
1099             "class Foo {", //
1100             "int x = 1;",
1101             "}");
1102     String expectedOutput =
1103         lines(
1104             "class Foo {", //
1105             "  int x = 1;",
1106             "}",
1107             "");
1108 
1109     Path tmpdir = testFolder.newFolder().toPath();
1110     Path path = tmpdir.resolve("Foo.java");
1111     Files.write(path, input.getBytes(UTF_8));
1112 
1113     StringWriter out = new StringWriter();
1114     StringWriter err = new StringWriter();
1115 
1116     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1117     String[] args = {"-lines", "1:5", path.toString()};
1118     assertThat(main.format(args)).isEqualTo(0);
1119     assertThat(out.toString()).isEqualTo(expectedOutput);
1120   }
1121 
1122   @Test
testOutOfRangeLines()1123   public void testOutOfRangeLines() throws Exception {
1124     String input =
1125         lines(
1126             "class Foo {", //
1127             "}",
1128             "");
1129     String expectedOutput =
1130         lines(
1131             "class Foo {}", //
1132             "");
1133 
1134     Path tmpdir = testFolder.newFolder().toPath();
1135     Path path = tmpdir.resolve("Foo.java");
1136     Files.write(path, input.getBytes(UTF_8));
1137 
1138     StringWriter out = new StringWriter();
1139     StringWriter err = new StringWriter();
1140 
1141     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1142     String[] args = {"-lines=23:27", "-lines=31:35", "-lines=52:63", "-lines=1:1", path.toString()};
1143     assertThat(main.format(args)).isEqualTo(0);
1144     assertThat(out.toString()).isEqualTo(expectedOutput);
1145   }
1146 
1147   @Test
testEmptyFirstLine()1148   public void testEmptyFirstLine() throws Exception {
1149     String input =
1150         lines(
1151             "", //
1152             "",
1153             "class Foo {",
1154             "}",
1155             "");
1156 
1157     Path tmpdir = testFolder.newFolder().toPath();
1158     Path path = tmpdir.resolve("Foo.java");
1159     Files.write(path, input.getBytes(UTF_8));
1160 
1161     StringWriter out = new StringWriter();
1162     StringWriter err = new StringWriter();
1163 
1164     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1165     String[] args = {"-lines=1:1", path.toString()};
1166     assertThat(main.format(args)).isEqualTo(0);
1167   }
1168 
1169   @Test
testEmptyLastLine()1170   public void testEmptyLastLine() throws Exception {
1171     String input =
1172         lines(
1173             "class Foo {", //
1174             "}",
1175             "",
1176             "");
1177 
1178     Path tmpdir = testFolder.newFolder().toPath();
1179     Path path = tmpdir.resolve("Foo.java");
1180     Files.write(path, input.getBytes(UTF_8));
1181 
1182     StringWriter out = new StringWriter();
1183     StringWriter err = new StringWriter();
1184 
1185     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1186     String[] args = {"-lines=5:5", path.toString()};
1187     assertThat(main.format(args)).isEqualTo(0);
1188   }
1189 
1190   // Regression test for b/22872933
1191   // Don't extend partial formatting ranges across switch cases.
1192   @Test
switchCase()1193   public void switchCase() throws Exception {
1194     String input =
1195         lines(
1196             "class Test {",
1197             "  {",
1198             "    switch (foo) {",
1199             "      case FOO:",
1200             "      f();",
1201             "      break;",
1202             "      case BAR:",
1203             "      g();",
1204             "      break;",
1205             "    }",
1206             "  }",
1207             "}");
1208     String expectedOutput =
1209         lines(
1210             "class Test {",
1211             "  {",
1212             "    switch (foo) {",
1213             "      case FOO:",
1214             "        f();",
1215             "      break;",
1216             "      case BAR:", // we deliberately only format the first case
1217             "      g();",
1218             "      break;",
1219             "    }",
1220             "  }",
1221             "}");
1222 
1223     int idx = input.indexOf("f()");
1224     String output = doGetFormatReplacements(input, idx, idx + 1);
1225     assertThat(output).isEqualTo(expectedOutput);
1226   }
1227 
1228   // regression test for b/23349153
1229   @Test
emptyStatement()1230   public void emptyStatement() throws Exception {
1231     String input =
1232         lines(
1233             "class Test {{", //
1234             "Object o = f();;",
1235             "}}",
1236             "");
1237     String expectedOutput =
1238         lines(
1239             "class Test {{", //
1240             "    Object o = f();",
1241             "    ;",
1242             "}}",
1243             "");
1244     int idx = input.indexOf("Object o");
1245     String output = doGetFormatReplacements(input, idx, idx + 1);
1246     assertThat(output).isEqualTo(expectedOutput);
1247   }
1248 
1249   @Test
preserveTrailingWhitespaceAfterNewline()1250   public void preserveTrailingWhitespaceAfterNewline() throws Exception {
1251     String input =
1252         lines(
1253             "class Test {{", //
1254             "Object o = f();       ",
1255             "            int x;",
1256             "}}",
1257             "");
1258     String expectedOutput =
1259         lines(
1260             "class Test {{", //
1261             "    Object o = f();",
1262             "            int x;",
1263             "}}",
1264             "");
1265     int idx = input.indexOf("Object o");
1266     String output = doGetFormatReplacements(input, idx, idx + 1);
1267     assertThat(output).isEqualTo(expectedOutput);
1268   }
1269 
1270   @Test
trailingWhitespace()1271   public void trailingWhitespace() throws Exception {
1272     String input =
1273         lines(
1274             "class Test {{", //
1275             "Object o = f();       ",
1276             "            ;",
1277             "}}",
1278             "");
1279     String expectedOutput =
1280         lines(
1281             "class Test {{", //
1282             "    Object o = f();",
1283             "            ;",
1284             "}}",
1285             "");
1286     int idx = input.indexOf("Object o");
1287     String output = doGetFormatReplacements(input, idx, idx + 1);
1288     assertThat(output).isEqualTo(expectedOutput);
1289   }
1290 
1291   // Regression test for b/18479811
1292   @Test
onNewline()1293   public void onNewline() throws Exception {
1294 
1295     String line1 = "for (Integer x : Arrays.asList(1, 2, 3)) {";
1296     String line2 = "System.err.println(x);";
1297     String input =
1298         lines(
1299             "class Test {{", //
1300             line1,
1301             line2,
1302             "}}}",
1303             "");
1304 
1305     int startOffset = input.indexOf(line1);
1306     int length = 1;
1307 
1308     String expectedFormatLine1 =
1309         lines(
1310             "class Test {{",
1311             "    for (Integer x : Arrays.asList(1, 2, 3)) {",
1312             "System.err.println(x);",
1313             "}}}",
1314             "");
1315 
1316     for (; length <= line1.length() + newline.length(); length++) {
1317       Range<Integer> range = Range.closedOpen(startOffset, startOffset + length);
1318       String output = new Formatter().formatSource(input, ImmutableList.of(range));
1319       assertEquals("bad output", expectedFormatLine1, output);
1320     }
1321 
1322     String expectedFormatLine1And2 =
1323         lines(
1324             "class Test {{",
1325             "    for (Integer x : Arrays.asList(1, 2, 3)) {",
1326             "      System.err.println(x);",
1327             "}}}",
1328             "");
1329 
1330     for (; length <= line1.length() + line2.length() + 2 * newline.length(); length++) {
1331       Range<Integer> range = Range.closedOpen(startOffset, startOffset + length);
1332       String output = new Formatter().formatSource(input, ImmutableList.of(range));
1333       assertEquals("bad output", expectedFormatLine1And2, output);
1334     }
1335   }
1336 
1337   @Test
afterNewline()1338   public void afterNewline() throws Exception {
1339 
1340     String line1 = "for (Integer x : Arrays.asList(1, 2, 3)) {";
1341     String line2 = "                  System.err.println(x);";
1342     String input =
1343         lines(
1344             "class Test {{", //
1345             line1,
1346             line2,
1347             "}}}",
1348             "");
1349 
1350     String expectedFormatLine1 =
1351         lines(
1352             "class Test {{", //
1353             "    for (Integer x : Arrays.asList(1, 2, 3)) {", //
1354             line2,
1355             "}}}",
1356             "");
1357 
1358     String expectedFormatLine2 =
1359         lines(
1360             "class Test {{", //
1361             line1,
1362             "      System.err.println(x);",
1363             "}}}",
1364             "");
1365 
1366     int line2Start = input.indexOf(line2);
1367     int nonWhitespaceLine2Start = input.indexOf("System.err");
1368     int start;
1369     // formatting a range that touches non-whitespace characters in line2 should format line2
1370     for (start = nonWhitespaceLine2Start; start > (line2Start - newline.length()); start--) {
1371       Range<Integer> range = Range.closedOpen(start, nonWhitespaceLine2Start + newline.length());
1372       String output = new Formatter().formatSource(input, ImmutableList.of(range));
1373       assertThat(output).isEqualTo(expectedFormatLine2);
1374     }
1375     // formatting a range that touches whitespace characters between line1 and line2 should
1376     // not result in any formatting
1377     assertThat(input.substring(start, start + newline.length())).isEqualTo(newline);
1378     int line1End = input.indexOf(line1) + line1.length();
1379     for (; start >= line1End; start--) {
1380       Range<Integer> range = Range.closedOpen(start, line2Start);
1381       String output = new Formatter().formatSource(input, ImmutableList.of(range));
1382       assertThat(output).isEqualTo(input);
1383     }
1384     // formatting a range that touches non-whitespace characters in line1 should format line1
1385     assertThat(input.substring(start + 1, start + 1 + newline.length())).isEqualTo(newline);
1386     int line1Start = input.indexOf(line1);
1387     for (; start >= line1Start; start--) {
1388       Range<Integer> range = Range.closedOpen(start, line2Start);
1389       String output = new Formatter().formatSource(input, ImmutableList.of(range));
1390       assertThat(output).isEqualTo(expectedFormatLine1);
1391     }
1392   }
1393 
1394   @Test
commentBeforeBadConstructor()1395   public void commentBeforeBadConstructor() throws Exception {
1396     String[] lines = {
1397       "class D {", //
1398       "  /** */",
1399       "  F() {}",
1400       "}",
1401     };
1402     String output = new Formatter().formatSource(lines(lines));
1403     String[] expected = {
1404       "class D {", //
1405       "  /** */",
1406       "  F() {}",
1407       "}",
1408       "",
1409     };
1410     assertThat(output).isEqualTo(lines(expected));
1411   }
1412 
1413   @Test
partialEnum()1414   public void partialEnum() throws Exception {
1415     String[] input = {
1416       "enum E {", //
1417       "ONE,",
1418       "TWO,",
1419       "THREE;",
1420       "}",
1421     };
1422     String[] expected = {
1423       "enum E {", //
1424       "ONE,",
1425       "  TWO,",
1426       "THREE;",
1427       "}",
1428     };
1429 
1430     Path tmpdir = testFolder.newFolder().toPath();
1431     Path path = tmpdir.resolve("Foo.java");
1432     Files.write(path, lines(input).getBytes(UTF_8));
1433 
1434     StringWriter out = new StringWriter();
1435     StringWriter err = new StringWriter();
1436 
1437     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1438     String[] args = {"-lines", "3", path.toString()};
1439     assertThat(main.format(args)).isEqualTo(0);
1440     assertThat(out.toString()).isEqualTo(lines(expected));
1441   }
1442 
1443   @Test
partialModifierOrder()1444   public void partialModifierOrder() throws Exception {
1445     String[] input = {
1446       "class T {", //
1447       "final private int a = 0;",
1448       "final private int b = 0;",
1449       "final private int c = 0;",
1450       "}",
1451     };
1452     String[] expected = {
1453       "class T {", //
1454       "final private int a = 0;",
1455       "  private final int b = 0;",
1456       "final private int c = 0;",
1457       "}",
1458     };
1459 
1460     Path tmpdir = testFolder.newFolder().toPath();
1461     Path path = tmpdir.resolve("Foo.java");
1462     Files.write(path, lines(input).getBytes(UTF_8));
1463 
1464     StringWriter out = new StringWriter();
1465     StringWriter err = new StringWriter();
1466 
1467     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1468     String[] args = {"-lines", "3", path.toString()};
1469     assertThat(main.format(args)).isEqualTo(0);
1470     assertThat(out.toString()).isEqualTo(lines(expected));
1471   }
1472 
1473   @Test
endOfLine()1474   public void endOfLine() throws Exception {
1475     String[] input = {
1476       "class foo {",
1477       "  foo(",
1478       "      int aaaaaaaaaaaaaaa,",
1479       "      int ccccccccccccc) {",
1480       "    int a = 0;",
1481       "    int c = 0;",
1482       "  }",
1483       "}",
1484     };
1485     String[] expected = {
1486       "class foo {",
1487       "  foo(int aaaaaaaaaaaaaaa, int ccccccccccccc) {",
1488       "    int a = 0;",
1489       "    int c = 0;",
1490       "  }",
1491       "}",
1492     };
1493     String in = lines(input);
1494     // request partial formatting of the end of the first parameter
1495     int start = in.indexOf(lines(",", "      int ccccccccccccc"));
1496     assertThat(in.substring(start, start + 1)).isEqualTo(",");
1497 
1498     assertThat(new Formatter().formatSource(in, ImmutableList.of(Range.closedOpen(start, start))))
1499         .isEqualTo(lines(expected));
1500 
1501     assertThat(formatMain(lines(input), "-offset", String.valueOf(start), "-length", "0"))
1502         .isEqualTo(lines(expected));
1503   }
1504 
formatMain(String input, String... args)1505   private String formatMain(String input, String... args) throws Exception {
1506     Path tmpdir = testFolder.newFolder().toPath();
1507     Path path = tmpdir.resolve("Test.java");
1508     Files.write(path, input.getBytes(UTF_8));
1509 
1510     StringWriter out = new StringWriter();
1511     StringWriter err = new StringWriter();
1512 
1513     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1514     assertThat(main.format(ObjectArrays.concat(args, path.toString()))).isEqualTo(0);
1515     return out.toString();
1516   }
1517 
1518   // formatting the newlinea after a statement is a no-op
1519   @Test
endOfLineStatement()1520   public void endOfLineStatement() throws Exception {
1521     String[] input = {
1522       "class foo {{", //
1523       "  int a = 0; ",
1524       "  int c = 0;",
1525       "}}",
1526     };
1527     String[] expected = {
1528       "class foo {{", //
1529       "    int a = 0;",
1530       "  int c = 0;",
1531       "}}",
1532     };
1533     String in = lines(input);
1534     int idx = in.indexOf(';');
1535     assertThat(new Formatter().formatSource(in, ImmutableList.of(Range.closedOpen(idx, idx))))
1536         .isEqualTo(lines(expected));
1537   }
1538 
1539   // formatting trailing whitespace at the end of the line doesn't format the line on either side
1540   @Test
endOfLineStatementNewline()1541   public void endOfLineStatementNewline() throws Exception {
1542     String[] input = {
1543       "class foo {{", //
1544       "  int a = 0; ",
1545       "  int c = 0;",
1546       "}}",
1547     };
1548     String in = lines(input);
1549     int idx = in.indexOf(';');
1550     assertThat(
1551             new Formatter().formatSource(in, ImmutableList.of(Range.closedOpen(idx + 1, idx + 1))))
1552         .isEqualTo(in);
1553   }
1554 
1555   @Test
importNewlines()1556   public void importNewlines() throws Exception {
1557     String input =
1558         lines(
1559             "package p;",
1560             "import java.util.ArrayList;",
1561             "class Foo {",
1562             "  ArrayList<String> xs = new ArrayList<>();",
1563             "}",
1564             "");
1565     String expectedOutput =
1566         lines(
1567             "package p;",
1568             "",
1569             "import java.util.ArrayList;",
1570             "",
1571             "class Foo {",
1572             "  ArrayList<String> xs = new ArrayList<>();",
1573             "}",
1574             "");
1575 
1576     String output = runFormatter(input, new String[] {"-lines", "2"});
1577     assertThat(output).isEqualTo(expectedOutput);
1578   }
1579 
1580   @Test
b36458607()1581   public void b36458607() throws Exception {
1582     String input =
1583         lines(
1584             "// copyright",
1585             "",
1586             "package p;",
1587             "import static c.g.I.c;",
1588             "",
1589             "/** */",
1590             "class Foo {{ c(); }}",
1591             "");
1592     String expectedOutput =
1593         lines(
1594             "// copyright",
1595             "",
1596             "package p;",
1597             "",
1598             "import static c.g.I.c;",
1599             "",
1600             "/** */",
1601             "class Foo {{ c(); }}",
1602             "");
1603 
1604     String output = runFormatter(input, new String[] {"-lines", "4"});
1605     assertThat(output).isEqualTo(expectedOutput);
1606   }
1607 
1608   @Test
b32159971()1609   public void b32159971() throws Exception {
1610     String input =
1611         lines(
1612             "", //
1613             "",
1614             "package p;",
1615             "class X {}",
1616             "");
1617     String expectedOutput =
1618         lines(
1619             "package p;", //
1620             "",
1621             "class X {}",
1622             "");
1623 
1624     String output = runFormatter(input, new String[] {"-lines", "3"});
1625     assertThat(output).isEqualTo(expectedOutput);
1626   }
1627 
1628   @Test
b21668189()1629   public void b21668189() throws Exception {
1630     String input =
1631         lines(
1632             "class Foo {", //
1633             "  {",
1634             "    int x = 1;",
1635             "    ",
1636             "    int y = 2;",
1637             "  }",
1638             "}",
1639             "");
1640     String expectedOutput =
1641         lines(
1642             "class Foo {", //
1643             "  {",
1644             "    int x = 1;",
1645             "",
1646             "    int y = 2;",
1647             "  }",
1648             "}",
1649             "");
1650 
1651     String output = runFormatter(input, new String[] {"-lines", "4:5"});
1652     assertThat(output).isEqualTo(expectedOutput);
1653   }
1654 
runFormatter(String input, String[] args)1655   private String runFormatter(String input, String[] args) throws IOException, UsageException {
1656     Path tmpdir = testFolder.newFolder().toPath();
1657     Path path = tmpdir.resolve("Foo.java");
1658     Files.write(path, input.getBytes(UTF_8));
1659 
1660     StringWriter out = new StringWriter();
1661     StringWriter err = new StringWriter();
1662 
1663     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
1664     assertThat(main.format(ObjectArrays.concat(args, path.toString()))).isEqualTo(0);
1665     return out.toString();
1666   }
1667 
1668   @Test
trailing()1669   public void trailing() throws Exception {
1670     String input =
1671         lines(
1672             "package foo.bar.baz;",
1673             "",
1674             "public class B {",
1675             "  public void f() {",
1676             "    int a = 7 +4;",
1677             "    int b = 7 +4;",
1678             "    int c = 7 +4;",
1679             "    int d = 7 +4;",
1680             "",
1681             "    int e = 7 +4;",
1682             "  }",
1683             "}");
1684     String expected =
1685         lines(
1686             "package foo.bar.baz;",
1687             "",
1688             "public class B {",
1689             "  public void f() {",
1690             "    int a = 7 +4;",
1691             "    int b = 7 +4;",
1692             "    int c = 7 + 4;",
1693             "    int d = 7 +4;",
1694             "",
1695             "    int e = 7 +4;",
1696             "  }",
1697             "}");
1698     String actual =
1699         new Formatter().formatSource(input, ImmutableList.of(rangeOf(input, "int c = 7 +4")));
1700     assertThat(actual).isEqualTo(expected);
1701   }
1702 
rangeOf(String input, String needle)1703   private Range<Integer> rangeOf(String input, String needle) {
1704     int idx = input.indexOf(needle);
1705     return Range.closedOpen(idx, idx + needle.length());
1706   }
1707 
1708   @Test
importJavadocNewlines()1709   public void importJavadocNewlines() throws Exception {
1710     String input =
1711         lines(
1712             "package p;",
1713             "import java.util.ArrayList;",
1714             "/** */",
1715             "class Foo {",
1716             "  ArrayList<String> xs = new ArrayList<>();",
1717             "}",
1718             "");
1719     String expectedOutput =
1720         lines(
1721             "package p;",
1722             "",
1723             "import java.util.ArrayList;",
1724             "",
1725             "/** */",
1726             "class Foo {",
1727             "  ArrayList<String> xs = new ArrayList<>();",
1728             "}",
1729             "");
1730 
1731     String output = runFormatter(input, new String[] {"-lines", "2"});
1732     assertThat(output).isEqualTo(expectedOutput);
1733   }
1734 
1735   @Test
nestedSwitchCase()1736   public void nestedSwitchCase() throws Exception {
1737     String input =
1738         lines(
1739             "class Test {",
1740             "  {",
1741             "    switch (foo) {",
1742             "      case FOO:",
1743             "      f();",
1744             "      break;",
1745             "      case BAR:",
1746             "      switch (bar) {",
1747             "        case BAZ:",
1748             "        h();",
1749             "        break;",
1750             "        case BOZ:",
1751             "        i();",
1752             "        break;",
1753             "      }",
1754             "    }",
1755             "  }",
1756             "}");
1757     String expectedOutput =
1758         lines(
1759             "class Test {",
1760             "  {",
1761             "    switch (foo) {",
1762             "      case FOO:",
1763             "      f();",
1764             "      break;",
1765             "      case BAR:",
1766             "      switch (bar) {",
1767             "        case BAZ:",
1768             "            h();",
1769             "        break;",
1770             "        case BOZ:",
1771             "        i();",
1772             "        break;",
1773             "      }",
1774             "    }",
1775             "  }",
1776             "}");
1777 
1778     int idx = input.indexOf("h()");
1779     String output = doGetFormatReplacements(input, idx, idx + 1);
1780     assertThat(output).isEqualTo(expectedOutput);
1781   }
1782 
1783   @Test
b117602702()1784   public void b117602702() throws Exception {
1785     String input =
1786         lines(
1787             "class Foo {", //
1788             "private Foo () {};",
1789             "}",
1790             "");
1791     String expectedOutput =
1792         lines(
1793             "class Foo {", //
1794             "  private Foo() {}",
1795             "  ;",
1796             "}",
1797             "");
1798 
1799     String output = runFormatter(input, new String[] {"-offset", "13", "-length", "1"});
1800     assertThat(output).isEqualTo(expectedOutput);
1801   }
1802 }
1803