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 com.google.common.truth.Truth.assertWithMessage;
19 import static java.nio.charset.StandardCharsets.UTF_8;
20 import static org.junit.Assert.fail;
21 
22 import com.google.common.base.Joiner;
23 import com.google.common.io.CharStreams;
24 import com.google.googlejavaformat.java.JavaFormatterOptions.Style;
25 import java.io.ByteArrayInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.io.PrintWriter;
30 import java.io.StringWriter;
31 import java.nio.file.Files;
32 import java.nio.file.Path;
33 import org.junit.Rule;
34 import org.junit.Test;
35 import org.junit.rules.TemporaryFolder;
36 import org.junit.runner.RunWith;
37 import org.junit.runners.JUnit4;
38 
39 /** Integration test for google-java-format. */
40 @RunWith(JUnit4.class)
41 public final class FormatterTest {
42 
43   @Rule public TemporaryFolder testFolder = new TemporaryFolder();
44 
45   @Test
testFormatAosp()46   public void testFormatAosp() throws Exception {
47     // don't forget to misspell "long", or you will be mystified for a while
48     String input =
49         "class A{void b(){while(true){weCanBeCertainThatThisWillEndUpGettingWrapped("
50             + "because, it, is, just, so, very, very, very, very, looong);}}}";
51     String expectedOutput =
52         Joiner.on("\n")
53             .join(
54                 "class A {",
55                 "    void b() {",
56                 "        while (true) {",
57                 "            weCanBeCertainThatThisWillEndUpGettingWrapped(",
58                 "                    because, it, is, just, so, very, very, very, very, looong);",
59                 "        }",
60                 "    }",
61                 "}",
62                 "");
63 
64     Path tmpdir = testFolder.newFolder().toPath();
65     Path path = tmpdir.resolve("A.java");
66     Files.write(path, input.getBytes(UTF_8));
67 
68     StringWriter out = new StringWriter();
69     StringWriter err = new StringWriter();
70 
71     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
72     String[] args = {"--aosp", path.toString()};
73     assertThat(main.format(args)).isEqualTo(0);
74     assertThat(out.toString()).isEqualTo(expectedOutput);
75   }
76 
77   @Test
testFormatNonJavaFiles()78   public void testFormatNonJavaFiles() throws Exception {
79     StringWriter out = new StringWriter();
80     StringWriter err = new StringWriter();
81     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
82 
83     // should succeed because non-Java files are skipped
84     assertThat(main.format("foo.go")).isEqualTo(0);
85     assertThat(err.toString()).contains("Skipping non-Java file: " + "foo.go");
86 
87     // format still fails on missing files
88     assertThat(main.format("Foo.java")).isEqualTo(1);
89     assertThat(err.toString()).contains("Foo.java: could not read file: ");
90   }
91 
92   @Test
testFormatStdinStdoutWithDashFlag()93   public void testFormatStdinStdoutWithDashFlag() throws Exception {
94     String input = "class Foo{\n" + "void f\n" + "() {\n" + "}\n" + "}\n";
95     String expectedOutput = "class Foo {\n" + "  void f() {}\n" + "}\n";
96 
97     InputStream in = new ByteArrayInputStream(input.getBytes(UTF_8));
98     StringWriter out = new StringWriter();
99     StringWriter err = new StringWriter();
100 
101     InputStream oldIn = System.in;
102     System.setIn(in);
103 
104     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
105     assertThat(main.format("-")).isEqualTo(0);
106     assertThat(out.toString()).isEqualTo(expectedOutput);
107 
108     System.setIn(oldIn);
109   }
110 
111   @Test
testFormatLengthUpToEOF()112   public void testFormatLengthUpToEOF() throws Exception {
113     String input = "class Foo{\n" + "void f\n" + "() {\n" + "}\n" + "}\n\n\n\n\n\n";
114     String expectedOutput = "class Foo {\n" + "  void f() {}\n" + "}\n";
115 
116     Path tmpdir = testFolder.newFolder().toPath();
117     Path path = tmpdir.resolve("Foo.java");
118     Files.write(path, input.getBytes(UTF_8));
119 
120     StringWriter out = new StringWriter();
121     StringWriter err = new StringWriter();
122 
123     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
124     String[] args = {"--offset", "0", "--length", String.valueOf(input.length()), path.toString()};
125     assertThat(main.format(args)).isEqualTo(0);
126     assertThat(out.toString()).isEqualTo(expectedOutput);
127   }
128 
129   @Test
testFormatLengthOutOfRange()130   public void testFormatLengthOutOfRange() throws Exception {
131     String input = "class Foo{}\n";
132 
133     Path tmpdir = testFolder.newFolder().toPath();
134     Path path = tmpdir.resolve("Foo.java");
135     Files.write(path, input.getBytes(UTF_8));
136 
137     StringWriter out = new StringWriter();
138     StringWriter err = new StringWriter();
139 
140     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
141     String[] args = {"--offset", "0", "--length", "9999", path.toString()};
142     assertThat(main.format(args)).isEqualTo(1);
143     assertThat(err.toString())
144         .contains("error: invalid length 9999, offset + length (9999) is outside the file");
145   }
146 
147   @Test
blankInClassBody()148   public void blankInClassBody() throws FormatterException {
149     String input = "package test;\nclass T {\n\n}\n";
150     String output = new Formatter().formatSource(input);
151     String expect = "package test;\n\nclass T {}\n";
152     assertThat(output).isEqualTo(expect);
153   }
154 
155   @Test
blankInClassBodyNoTrailing()156   public void blankInClassBodyNoTrailing() throws FormatterException {
157     String input = "package test;\nclass T {\n\n}";
158     String output = new Formatter().formatSource(input);
159     String expect = "package test;\n\nclass T {}\n";
160     assertThat(output).isEqualTo(expect);
161   }
162 
163   @Test
docCommentTrailingBlank()164   public void docCommentTrailingBlank() throws FormatterException {
165     String input = "class T {\n/** asd */\n\nint x;\n}";
166     String output = new Formatter().formatSource(input);
167     String expect = "class T {\n  /** asd */\n  int x;\n}\n";
168     assertThat(output).isEqualTo(expect);
169   }
170 
171   @Test
blockCommentInteriorTrailingBlank()172   public void blockCommentInteriorTrailingBlank() throws FormatterException {
173     String input = "class T {\n/*\n* asd \n* fgh\n*/ \n\nint x;\n}";
174     String output = new Formatter().formatSource(input);
175     String expect = "class T {\n  /*\n   * asd\n   * fgh\n   */\n\n  int x;\n}\n";
176     assertThat(output).isEqualTo(expect);
177   }
178 
179   @Test
blockCommentTrailingBlank()180   public void blockCommentTrailingBlank() throws FormatterException {
181     String input = "class T {\n/* asd */ \n\nint x;\n}";
182     String output = new Formatter().formatSource(input);
183     String expect = "class T {\n  /* asd */\n\n  int x;\n}\n";
184     assertThat(output).isEqualTo(expect);
185   }
186 
187   @Test
lineCommentTrailingBlank()188   public void lineCommentTrailingBlank() throws FormatterException {
189     String input = "class T {\n// asd \n\nint x;\n}";
190     String output = new Formatter().formatSource(input);
191     String expect = "class T {\n  // asd\n\n  int x;\n}\n";
192     assertThat(output).isEqualTo(expect);
193   }
194 
195   @Test
lineCommentTrailingThinSpace()196   public void lineCommentTrailingThinSpace() throws FormatterException {
197     // The Unicode thin space is matched by CharMatcher.whitespace() but not trim().
198     String input = "class T {\n  // asd\u2009\n}\n";
199     String output = new Formatter().formatSource(input);
200     String expect = "class T {\n  // asd\n}\n";
201     assertThat(output).isEqualTo(expect);
202   }
203 
204   @Test
noBlankAfterLineCommentWithInteriorBlankLine()205   public void noBlankAfterLineCommentWithInteriorBlankLine() throws FormatterException {
206     String input = "class T {\n// asd \n\n// dsa \nint x;\n}";
207     String output = new Formatter().formatSource(input);
208     String expect = "class T {\n  // asd\n\n  // dsa\n  int x;\n}\n";
209     assertThat(output).isEqualTo(expect);
210   }
211 
212   @Test
badConstructor()213   public void badConstructor() throws FormatterException {
214     String input = "class X { Y() {} }";
215     String output = new Formatter().formatSource(input);
216     String expect = "class X {\n  Y() {}\n}\n";
217     assertThat(output).isEqualTo(expect);
218   }
219 
220   @Test
voidMethod()221   public void voidMethod() throws FormatterException {
222     String input = "class X { void Y() {} }";
223     String output = new Formatter().formatSource(input);
224     String expect = "class X {\n  void Y() {}\n}\n";
225     assertThat(output).isEqualTo(expect);
226   }
227 
228   private static final String UNORDERED_IMPORTS =
229       Joiner.on('\n')
230           .join(
231               "import com.google.common.base.Preconditions;",
232               "",
233               "import static org.junit.Assert.fail;",
234               "import static com.google.truth.Truth.assertThat;",
235               "",
236               "import org.junit.runners.JUnit4;",
237               "import org.junit.runner.RunWith;",
238               "",
239               "import java.util.List;",
240               "",
241               "import javax.annotation.Nullable;");
242 
243   @Test
importsNotReorderedByDefault()244   public void importsNotReorderedByDefault() throws FormatterException {
245     String input =
246         "package com.google.example;\n" + UNORDERED_IMPORTS + "\npublic class ExampleTest {}\n";
247     String output = new Formatter().formatSource(input);
248     String expect =
249         "package com.google.example;\n\n" + UNORDERED_IMPORTS + "\n\npublic class ExampleTest {}\n";
250     assertThat(output).isEqualTo(expect);
251   }
252 
253   @Test
importsFixedIfRequested()254   public void importsFixedIfRequested() throws FormatterException {
255     String input =
256         "package com.google.example;\n"
257             + UNORDERED_IMPORTS
258             + "\npublic class ExampleTest {\n"
259             + "  @Nullable List<?> xs;\n"
260             + "}\n";
261     String output = new Formatter().formatSourceAndFixImports(input);
262     String expect =
263         "package com.google.example;\n\n"
264             + "import java.util.List;\n"
265             + "import javax.annotation.Nullable;\n\n"
266             + "public class ExampleTest {\n"
267             + "  @Nullable List<?> xs;\n"
268             + "}\n";
269     assertThat(output).isEqualTo(expect);
270   }
271 
272   @Test
importOrderingWithoutFormatting()273   public void importOrderingWithoutFormatting() throws IOException, UsageException {
274     importOrdering(
275         "--fix-imports-only", "com/google/googlejavaformat/java/testimports/A.imports-only");
276   }
277 
278   @Test
importOrderingAndFormatting()279   public void importOrderingAndFormatting() throws IOException, UsageException {
280     importOrdering(null, "com/google/googlejavaformat/java/testimports/A.imports-and-formatting");
281   }
282 
283   @Test
formattingWithoutImportOrdering()284   public void formattingWithoutImportOrdering() throws IOException, UsageException {
285     importOrdering(
286         "--skip-sorting-imports",
287         "com/google/googlejavaformat/java/testimports/A.formatting-and-unused-import-removal");
288   }
289 
290   @Test
formattingWithoutRemovingUnusedImports()291   public void formattingWithoutRemovingUnusedImports() throws IOException, UsageException {
292     importOrdering(
293         "--skip-removing-unused-imports",
294         "com/google/googlejavaformat/java/testimports/A.formatting-and-import-sorting");
295   }
296 
importOrdering(String sortArg, String outputResourceName)297   private void importOrdering(String sortArg, String outputResourceName)
298       throws IOException, UsageException {
299     Path tmpdir = testFolder.newFolder().toPath();
300     Path path = tmpdir.resolve("Foo.java");
301 
302     String inputResourceName = "com/google/googlejavaformat/java/testimports/A.input";
303     String input = getResource(inputResourceName);
304     String expectedOutput = getResource(outputResourceName);
305     Files.write(path, input.getBytes(UTF_8));
306 
307     StringWriter out = new StringWriter();
308     StringWriter err = new StringWriter();
309     Main main = new Main(new PrintWriter(out, true), new PrintWriter(err, true), System.in);
310     String[] args =
311         sortArg != null
312             ? new String[] {sortArg, "-i", path.toString()}
313             : new String[] {"-i", path.toString()};
314     main.format(args);
315 
316     assertThat(err.toString()).isEmpty();
317     assertThat(out.toString()).isEmpty();
318     String output = new String(Files.readAllBytes(path), UTF_8);
319     assertThat(output).isEqualTo(expectedOutput);
320   }
321 
getResource(String resourceName)322   private String getResource(String resourceName) throws IOException {
323     try (InputStream stream = getClass().getClassLoader().getResourceAsStream(resourceName)) {
324       assertWithMessage("Missing resource: " + resourceName).that(stream).isNotNull();
325       return CharStreams.toString(new InputStreamReader(stream, UTF_8));
326     }
327   }
328 
329   // regression test for google-java-format#47
330   @Test
testTrailingCommentWithoutTerminalNewline()331   public void testTrailingCommentWithoutTerminalNewline() throws Exception {
332     assertThat(new Formatter().formatSource("/*\n * my comment */"))
333         .isEqualTo("/*\n * my comment */\n");
334   }
335 
336   @Test
testEmptyArray()337   public void testEmptyArray() throws Exception {
338     assertThat(new Formatter().formatSource("class T { int x[] = {,}; }"))
339         .isEqualTo("class T {\n  int x[] = {,};\n}\n");
340   }
341 
342   @Test
stringEscapeLength()343   public void stringEscapeLength() throws Exception {
344     assertThat(new Formatter().formatSource("class T {{ f(\"\\\"\"); }}"))
345         .isEqualTo("class T {\n  {\n    f(\"\\\"\");\n  }\n}\n");
346   }
347 
348   @Test
wrapLineComment()349   public void wrapLineComment() throws Exception {
350     assertThat(
351             new Formatter()
352                 .formatSource(
353                     "class T {\n"
354                         + "  public static void main(String[] args) { // one long incredibly"
355                         + " unbroken sentence moving from topic to topic so that no-one had a"
356                         + " chance to interrupt;\n"
357                         + "  }\n"
358                         + "}\n"))
359         .isEqualTo(
360             "class T {\n"
361                 + "  public static void main(\n"
362                 + "      String[]\n"
363                 + "          args) { // one long incredibly unbroken sentence moving"
364                 + " from topic to topic so that no-one\n"
365                 + "                  // had a chance to interrupt;\n"
366                 + "  }\n"
367                 + "}\n");
368   }
369 
370   @Test
onlyWrapLineCommentOnWhitespace()371   public void onlyWrapLineCommentOnWhitespace() throws Exception {
372     assertThat(
373             new Formatter()
374                 .formatSource(
375                     "class T {\n"
376                         + "  public static void main(String[] args) { // one_long_incredibly"
377                         + "_unbroken_sentence_moving_from_topic_to_topic_so_that_no-one_had_a"
378                         + "_chance_to_interrupt;\n"
379                         + "  }\n"
380                         + "}\n"))
381         .isEqualTo(
382             "class T {\n"
383                 + "  public static void main(\n"
384                 + "      String[]\n"
385                 + "          args) { // one_long_incredibly"
386                 + "_unbroken_sentence_moving_from_topic_to_topic_so_that_no-one_had_a"
387                 + "_chance_to_interrupt;\n"
388                 + "  }\n"
389                 + "}\n");
390   }
391 
392   @Test
onlyWrapLineCommentOnWhitespace_noLeadingWhitespace()393   public void onlyWrapLineCommentOnWhitespace_noLeadingWhitespace() throws Exception {
394     assertThat(
395             new Formatter()
396                 .formatSource(
397                     "class T {\n"
398                         + "  public static void main(String[] args) { //one_long_incredibly"
399                         + "_unbroken_sentence_moving_from_topic_to_topic_so_that_no-one_had_a"
400                         + "_chance_to_interrupt;\n"
401                         + "  }\n"
402                         + "}\n"))
403         .isEqualTo(
404             "class T {\n"
405                 + "  public static void main(\n"
406                 + "      String[]\n"
407                 + "          args) { // one_long_incredibly"
408                 + "_unbroken_sentence_moving_from_topic_to_topic_so_that_no-one_had_a"
409                 + "_chance_to_interrupt;\n"
410                 + "  }\n"
411                 + "}\n");
412   }
413 
414   @Test
throwsFormatterException()415   public void throwsFormatterException() throws Exception {
416     try {
417       new Formatter().formatSourceAndFixImports("package foo; public class {");
418       fail();
419     } catch (FormatterException expected) {
420     }
421   }
422 
423   @Test
blankLinesImportComment()424   public void blankLinesImportComment() throws FormatterException {
425     String withBlank =
426         "package p;\n"
427             + "\n"
428             + "/** test */\n"
429             + "\n"
430             + "import a.A;\n"
431             + "\n"
432             + "class T {\n"
433             + "  A a;\n"
434             + "}\n";
435     String withoutBlank =
436         "package p;\n"
437             + "\n"
438             + "/** test */\n"
439             + "import a.A;\n"
440             + "\n"
441             + "class T {\n"
442             + "  A a;\n"
443             + "}\n";
444 
445     // Formatting deletes the blank line between the "javadoc" and the first import.
446     assertThat(new Formatter().formatSource(withBlank)).isEqualTo(withoutBlank);
447     assertThat(new Formatter().formatSourceAndFixImports(withBlank)).isEqualTo(withoutBlank);
448     assertThat(new Formatter().formatSource(withoutBlank)).isEqualTo(withoutBlank);
449     assertThat(new Formatter().formatSourceAndFixImports(withoutBlank)).isEqualTo(withoutBlank);
450 
451     // Just fixing imports preserves whitespace around imports.
452     assertThat(RemoveUnusedImports.removeUnusedImports(withBlank)).isEqualTo(withBlank);
453     assertThat(ImportOrderer.reorderImports(withBlank, Style.GOOGLE)).isEqualTo(withBlank);
454     assertThat(RemoveUnusedImports.removeUnusedImports(withoutBlank)).isEqualTo(withoutBlank);
455     assertThat(ImportOrderer.reorderImports(withoutBlank, Style.GOOGLE)).isEqualTo(withoutBlank);
456   }
457 
458   @Test
dontWrapMoeLineComments()459   public void dontWrapMoeLineComments() throws Exception {
460     assertThat(
461             new Formatter()
462                 .formatSource(
463                     "class T {\n"
464                         + "  // MOE: one long incredibly"
465                         + " unbroken sentence moving from topic to topic so that no-one had a"
466                         + " chance to interrupt;\n"
467                         + "}\n"))
468         .isEqualTo(
469             "class T {\n"
470                 + "  // MOE: one long incredibly"
471                 + " unbroken sentence moving from topic to topic so that no-one had a"
472                 + " chance to interrupt;\n"
473                 + "}\n");
474   }
475 
476   @Test
removeTrailingTabsInComments()477   public void removeTrailingTabsInComments() throws Exception {
478     assertThat(
479             new Formatter()
480                 .formatSource(
481                     "class Foo {\n"
482                         + "  void f() {\n"
483                         + "    int x = 0; // comment\t\t\t\n"
484                         + "    return;\n"
485                         + "  }\n"
486                         + "}\n"))
487         .isEqualTo(
488             "class Foo {\n"
489                 + "  void f() {\n"
490                 + "    int x = 0; // comment\n"
491                 + "    return;\n"
492                 + "  }\n"
493                 + "}\n");
494   }
495 }
496