View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2026 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ///////////////////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.api;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
24  
25  import java.io.File;
26  import java.util.Arrays;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.junit.jupiter.api.Test;
33  
34  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
35  
36  public class FileContentsTest {
37  
38      @Test
39      public void testTextFileName() {
40          final FileContents fileContents = new FileContents(
41                  new FileText(new File("filename"), Arrays.asList("123", "456")));
42  
43          assertWithMessage("Invalid file name")
44                  .that(fileContents.getText().getFile().getName())
45                  .isEqualTo("filename");
46          assertWithMessage("Invalid array")
47                  .that(fileContents.getLines())
48                  .isEqualTo(new String[] {"123", "456"});
49          assertWithMessage("Invalid file name")
50                  .that(fileContents.getFileName())
51                  .isEqualTo("filename");
52      }
53  
54      @Test
55      public void testIsLineBlank() {
56          assertWithMessage("Invalid result")
57                  .that(new FileContents(
58                          new FileText(new File("filename"), Collections.singletonList("123")))
59                                  .lineIsBlank(0))
60                  .isFalse();
61          assertWithMessage("Invalid result")
62                  .that(new FileContents(new FileText(new File("filename"),
63                          Collections.singletonList("")))
64                          .lineIsBlank(0))
65                  .isTrue();
66      }
67  
68      @Test
69      public void testLineIsComment() {
70          assertWithMessage("Invalid result")
71                  .that(new FileContents(
72                          new FileText(new File("filename"), Collections.singletonList("123")))
73                                  .lineIsComment(0))
74                  .isFalse();
75          assertWithMessage("Invalid result")
76                  .that(new FileContents(
77                          new FileText(new File("filename"), Collections.singletonList(" // abc")))
78                                  .lineIsComment(0))
79                  .isTrue();
80      }
81  
82      @Test
83      public void testDeprecatedAbbreviatedMethod() {
84          // just to make UT coverage 100%
85          final FileContents fileContents = new FileContents(
86                  new FileText(new File("filename"), Arrays.asList("123", "456")));
87          fileContents.reportSingleLineComment(1, 1);
88          fileContents.reportBlockComment(1, 1, 1, 1);
89  
90          final Comment cppComment = new Comment(new String[] {"23"}, 1, 1, 2);
91          final Comment cComment = new Comment(new String[] {"2"}, 1, 1, 1);
92          final String lineComment = fileContents.getSingleLineComments().get(1).toString();
93          assertWithMessage("Invalid cpp comment")
94                  .that(lineComment)
95                  .isEqualTo(cppComment.toString());
96          final String blockComment = fileContents.getBlockComments().get(1).getFirst().toString();
97          assertWithMessage("Invalid c comment")
98                  .that(blockComment)
99                  .isEqualTo(cComment.toString());
100     }
101 
102     @Test
103     public void testSinglelineCommentNotIntersect() {
104         // just to make UT coverage 100%
105         final FileContents fileContents = new FileContents(
106                 new FileText(new File("filename"), Collections.singletonList("  //  ")));
107         fileContents.reportSingleLineComment(1, 2);
108         assertWithMessage("Should return false when there is no intersection")
109                 .that(fileContents.hasIntersectionWithComment(1, 0, 1, 1))
110                 .isFalse();
111     }
112 
113     @Test
114     public void testSinglelineCommentIntersect() {
115         // just to make UT coverage 100%
116         final FileContents fileContents = new FileContents(
117                 new FileText(new File("filename"), Collections.singletonList("  //   ")));
118         fileContents.reportSingleLineComment("type", 1, 2);
119         assertWithMessage("Should return true when comments intersect")
120                 .that(fileContents.hasIntersectionWithComment(1, 5, 1, 6))
121                 .isTrue();
122     }
123 
124     @Test
125     public void testReportCppComment() {
126         final FileContents fileContents = new FileContents(
127                 new FileText(new File("filename"), Collections.singletonList("   //  ")));
128         fileContents.reportSingleLineComment(1, 2);
129         final Map<Integer, TextBlock> cppComments = fileContents.getSingleLineComments();
130 
131         assertWithMessage("Invalid comment")
132                 .that(cppComments.get(1).toString())
133                 .isEqualTo(new Comment(new String[] {" //  "}, 2, 1, 6).toString());
134     }
135 
136     @Test
137     public void testHasIntersectionWithSingleLineComment() {
138         final FileContents fileContents = new FileContents(
139                 new FileText(new File("filename"), Arrays.asList("     ", "  //test   ",
140                         "  //test   ", "  //test   ")));
141         fileContents.reportSingleLineComment(4, 4);
142 
143         assertWithMessage("Should return true when comments intersect")
144                 .that(fileContents.hasIntersectionWithComment(1, 3, 4, 6))
145                 .isTrue();
146     }
147 
148     @Test
149     public void testReportComment() {
150         final FileContents fileContents = new FileContents(
151                 new FileText(new File("filename"), Collections.singletonList("  //   ")));
152         fileContents.reportBlockComment("type", 1, 2, 1, 2);
153         final Map<Integer, List<TextBlock>> comments = fileContents.getBlockComments();
154 
155         assertWithMessage("Invalid comment")
156                 .that(comments.get(1).getFirst().toString())
157                 .isEqualTo(new Comment(new String[] {"/"}, 2, 1, 2).toString());
158     }
159 
160     @Test
161     public void testReportBlockCommentSameLine() {
162         final FileContents fileContents = new FileContents(
163                 new FileText(new File("filename"), Collections.singletonList("/* a */ /* b */ ")));
164         fileContents.reportBlockComment("type", 1, 0, 1, 6);
165         fileContents.reportBlockComment("type", 1, 8, 1, 14);
166         final Map<Integer, List<TextBlock>> comments = fileContents.getBlockComments();
167 
168         assertWithMessage("Invalid comment")
169                 .that(comments.get(1).toString())
170                 .isEqualTo(Arrays.asList(
171                     new Comment(new String[] {"/* a */"}, 0, 1, 6),
172                     new Comment(new String[] {"/* b */"}, 8, 1, 14)
173                 ).toString());
174     }
175 
176     @Test
177     public void testReportBlockCommentMultiLine() {
178         final FileContents fileContents = new FileContents(
179                 new FileText(new File("filename"), Arrays.asList("/*", "c", "*/")));
180         fileContents.reportBlockComment("type", 1, 0, 3, 1);
181         final Map<Integer, List<TextBlock>> comments = fileContents.getBlockComments();
182 
183         assertWithMessage("Invalid comment")
184                 .that(comments.get(1).toString())
185                 .isEqualTo(Collections.singletonList(
186                     new Comment(new String[] {"/*", "c", "*/"}, 0, 3, 1)).toString()
187             );
188     }
189 
190     @Test
191     public void testReportBlockCommentJavadoc() {
192         final FileContents fileContents = new FileContents(new FileText(new File("filename"),
193                 Arrays.asList("/** A */", "", "//", "/**/", "/* B */")));
194         fileContents.reportBlockComment("type", 1, 0, 1, 7);
195         fileContents.reportBlockComment("type", 4, 0, 4, 3);
196         fileContents.reportBlockComment("type", 5, 0, 5, 6);
197 
198         assertWithMessage("Invalid comment")
199                 .that(fileContents.getJavadocBefore(1))
200                 .isNull();
201         assertWithMessage("Invalid comment")
202                 .that(fileContents.getJavadocBefore(4).toString())
203                 .isEqualTo(new Comment(new String[] {"/** A */"}, 0, 1, 7).toString());
204         assertWithMessage("Invalid comment")
205                 .that(fileContents.getJavadocBefore(5).toString())
206                 .isEqualTo(new Comment(new String[] {"/** A */"}, 0, 1, 7).toString());
207         assertWithMessage("Invalid comment")
208                 .that(fileContents.getJavadocBefore(6).toString())
209                 .isEqualTo(new Comment(new String[] {"/** A */"}, 0, 1, 7).toString());
210     }
211 
212     @Test
213     public void testHasIntersectionWithBlockComment() {
214         final FileContents fileContents = new FileContents(new FileText(new File("filename"),
215                         Arrays.asList("  /* */    ", "    ", "  /* test   ", "  */  ", "   ")));
216         fileContents.reportBlockComment(1, 2, 1, 5);
217         fileContents.reportBlockComment(3, 2, 4, 2);
218 
219         assertWithMessage("Should return true when comments intersect")
220                 .that(fileContents.hasIntersectionWithComment(2, 2, 3, 6))
221                 .isTrue();
222     }
223 
224     @Test
225     public void testHasIntersectionWithBlockComment2() {
226         final FileContents fileContents = new FileContents(
227                 new FileText(new File("filename"), Arrays.asList("  /* */    ", "    ", " ")));
228         fileContents.reportBlockComment(1, 2, 1, 5);
229 
230         assertWithMessage("Should return false when there is no intersection")
231                 .that(fileContents.hasIntersectionWithComment(2, 2, 3, 6))
232                 .isFalse();
233     }
234 
235     @Test
236     public void testReportJavadocComment() {
237         final FileContents fileContents = new FileContents(
238                 new FileText(new File("filename"), Collections.singletonList("  /** */   ")));
239         fileContents.reportBlockComment(1, 2, 1, 6);
240         final TextBlock comment = fileContents.getJavadocBefore(2);
241 
242         assertWithMessage("Invalid comment")
243                 .that(comment.toString())
244                 .isEqualTo(new Comment(new String[] {"/** *"}, 2, 1, 6).toString());
245     }
246 
247     @Test
248     public void testReportJavadocComment2() {
249         final FileContents fileContents = new FileContents(
250                 new FileText(new File("filename"), Collections.singletonList("  /** */   ")));
251         fileContents.reportBlockComment(1, 2, 1, 6);
252         final TextBlock comment = fileContents.getJavadocBefore(2);
253 
254         assertWithMessage("Invalid comment")
255                 .that(comment.toString())
256                 .isEqualTo(new Comment(new String[] {"/** *"}, 2, 1, 6).toString());
257     }
258 
259     /*
260      * This method is deprecated due to usage of deprecated FileContents#inPackageInfo
261      * we keep this method until https://github.com/checkstyle/checkstyle/issues/11723
262      */
263     @Deprecated(since = "10.2")
264     @Test
265     public void testInPackageInfo() {
266         final FileContents fileContents = new FileContents(new FileText(
267                 new File("package-info.java"),
268                 Collections.singletonList("  //   ")));
269 
270         assertWithMessage("Should return true when in package info")
271                 .that(fileContents.inPackageInfo())
272                 .isTrue();
273     }
274 
275     /*
276      * This method is deprecated due to usage of deprecated FileContents#inPackageInfo
277      * we keep this method until https://github.com/checkstyle/checkstyle/issues/11723
278      */
279     @Deprecated(since = "10.2")
280     @Test
281     public void testNotInPackageInfo() {
282         final FileContents fileContents = new FileContents(new FileText(
283                 new File("some-package-info.java"),
284                 Collections.singletonList("  //   ")));
285 
286         assertWithMessage("Should return false when not in package info")
287                 .that(fileContents.inPackageInfo())
288                 .isFalse();
289     }
290 
291     @Test
292     public void testGetJavadocBefore() {
293         final FileContents fileContents = new FileContents(
294                 new FileText(new File("filename"), Collections.singletonList("    ")));
295         final Map<Integer, TextBlock> javadoc = new HashMap<>();
296         javadoc.put(0, new Comment(new String[] {"// "}, 2, 1, 2));
297         TestUtil.setInternalState(fileContents, "javadocComments", javadoc);
298         final TextBlock javadocBefore = fileContents.getJavadocBefore(2);
299 
300         assertWithMessage("Invalid before javadoc")
301                 .that(javadocBefore.toString())
302                 .isEqualTo(new Comment(new String[] {"// "}, 2, 1, 2).toString());
303     }
304 
305     /**
306      * Pure unit test is used because this deprecated API is not covered by input-based checks.
307      */
308     @Test
309     public void testGetJavadocBeforeSkipsIndentedBlockComment() {
310         final List<String> lines = Arrays.asList(
311                 "final class Holder {",
312                 "  /** Javadoc. */",
313                 "  /* Block.",
314                 "   */   ",
315                 "  void method() {}",
316                 "}");
317         final FileContents fileContents = createFileContents(lines);
318         reportBlockComment(fileContents, lines, 2, 2);
319         reportBlockComment(fileContents, lines, 3, 4);
320 
321         assertWithMessage("Should skip full-line block comment before declaration")
322                 .that(fileContents.getJavadocBefore(5).toString())
323                 .isEqualTo(new Comment(new String[] {"/** Javadoc. */"}, 2, 2, 16)
324                         .toString());
325     }
326 
327     /**
328      * Pure unit test is used because this deprecated API is not covered by input-based checks.
329      */
330     @Test
331     public void testGetJavadocBeforeSkipsBlockCommentButNotCodeBeforeIt() {
332         final List<String> lines = Arrays.asList(
333                 "final class Holder {",
334                 "  /** Javadoc. */",
335                 "  int field;",
336                 "  /* Block. */",
337                 "  void method() {}",
338                 "}");
339         final FileContents fileContents = createFileContents(lines);
340         reportBlockComment(fileContents, lines, 2, 2);
341         reportBlockComment(fileContents, lines, 4, 4);
342 
343         assertWithMessage("Should stop searching on code before block comment")
344                 .that(fileContents.getJavadocBefore(5))
345                 .isNull();
346     }
347 
348     /**
349      * Pure unit test is used because this deprecated API is not covered by input-based checks.
350      */
351     @Test
352     public void testGetJavadocBeforeDoesNotSkipCodeAfterBlockComment() {
353         final List<String> lines = Arrays.asList(
354                 "final class Holder {",
355                 "  /** Javadoc. */",
356                 "  /* Block. */",
357                 "  int field;",
358                 "  void method() {}",
359                 "}");
360         final FileContents fileContents = createFileContents(lines);
361         reportBlockComment(fileContents, lines, 2, 2);
362         reportBlockComment(fileContents, lines, 3, 3);
363 
364         assertWithMessage("Should stop searching on code after block comment")
365                 .that(fileContents.getJavadocBefore(5))
366                 .isNull();
367     }
368 
369     /**
370      * Pure unit test is used because this deprecated API is not covered by input-based checks.
371      */
372     @Test
373     public void testGetJavadocBeforeDoesNotSkipBlockCommentWithCodeBeforeIt() {
374         final List<String> lines = Arrays.asList(
375                 "final class Holder {",
376                 "  /** Javadoc. */",
377                 "  int field; /* Block.",
378                 "   */",
379                 "  void method() {}",
380                 "}");
381         final FileContents fileContents = createFileContents(lines);
382         reportBlockComment(fileContents, lines, 2, 2);
383         reportBlockComment(fileContents, lines, 3, 4);
384 
385         assertWithMessage("Should stop searching on block comment with code before it")
386                 .that(fileContents.getJavadocBefore(5))
387                 .isNull();
388     }
389 
390     /**
391      * Pure unit test is used because this deprecated API is not covered by input-based checks.
392      */
393     @Test
394     public void testGetJavadocBeforeDoesNotSkipBlockCommentWithCodeAfterIt() {
395         final List<String> lines = Arrays.asList(
396                 "final class Holder {",
397                 "  /** Javadoc. */",
398                 "  /* Block.",
399                 "   */ int field;",
400                 "  void method() {}",
401                 "}");
402         final FileContents fileContents = createFileContents(lines);
403         reportBlockComment(fileContents, lines, 2, 2);
404         reportBlockComment(fileContents, lines, 3, 4);
405 
406         assertWithMessage("Should stop searching on block comment with code after it")
407                 .that(fileContents.getJavadocBefore(5))
408                 .isNull();
409     }
410 
411     /**
412      * Pure unit test is used because this deprecated API is not covered by input-based checks.
413      */
414     @Test
415     public void testGetJavadocBeforeDoesNotTreatJavadocAsBlockComment() {
416         final List<String> lines = Arrays.asList(
417                 "final class Holder {",
418                 "  int field;",
419                 "  /** Javadoc. */",
420                 "  /* Block. */",
421                 "  void method() {}",
422                 "}");
423         final FileContents fileContents = createFileContents(lines);
424         reportBlockComment(fileContents, lines, 3, 3);
425         reportBlockComment(fileContents, lines, 4, 4);
426 
427         assertWithMessage("Should stop searching at the closest Javadoc comment")
428                 .that(fileContents.getJavadocBefore(5).toString())
429                 .isEqualTo(new Comment(new String[] {"/** Javadoc. */"}, 2, 3, 16)
430                         .toString());
431     }
432 
433     @Test
434     public void testExtractBlockComment() {
435         final FileContents fileContents = new FileContents(
436                 new FileText(new File("filename"), Arrays.asList("   ", "    ", "  /* test   ",
437                         "  */  ", "   ")));
438         fileContents.reportBlockComment(3, 2, 4, 2);
439         final Map<Integer, List<TextBlock>> blockComments =
440             fileContents.getBlockComments();
441         final String[] text = blockComments.get(3).getFirst().getText();
442 
443         assertWithMessage("Invalid comment text")
444                 .that(text)
445                 .isEqualTo(new String[] {"/* test   ", "  *"});
446     }
447 
448     @Test
449     public void testHasIntersectionEarlyOut() throws Exception {
450         final FileContents fileContents = new FileContents(
451                 new FileText(new File("filename"), Collections.emptyList()));
452         final Map<Integer, List<TextBlock>> clangComments = TestUtil.getInternalStateMapIntegerList(
453                 fileContents, "clangComments");
454         final TextBlock textBlock = new Comment(new String[] {""}, 1, 1, 1);
455         clangComments.put(1, Collections.singletonList(textBlock));
456         clangComments.put(2, Collections.emptyList());
457 
458         assertWithMessage("Invalid results")
459                 .that(TestUtil.invokeMethod(fileContents,
460                         "hasIntersectionWithBlockComment", Boolean.class, 1, 1, 1, 1))
461                 .isTrue();
462     }
463 
464     @Test
465     public void testUnmodifiableGetSingleLineComment() {
466         final FileContents cppComments = new FileContents(new FileText(new File("filename"),
467                 Arrays.asList("// comment ", " A + B ", " ")));
468         cppComments.reportSingleLineComment(1, 0);
469         final Map<Integer, TextBlock> comments = cppComments.getSingleLineComments();
470         final Exception ex = getExpectedThrowable(UnsupportedOperationException.class,
471                 () -> comments.remove(0));
472         assertWithMessage("Exception message not expected")
473                 .that(ex.getClass())
474                 .isEqualTo(UnsupportedOperationException.class);
475     }
476 
477     @Test
478     public void testUnmodifiableGetBlockComments() {
479         final FileContents clangComments = new FileContents(new FileText(new File("filename"),
480                 Arrays.asList("/* comment ", " ", " comment */")));
481         clangComments.reportBlockComment(1, 0, 3, 9);
482         final Map<Integer, List<TextBlock>> comments = clangComments.getBlockComments();
483         final Exception ex = getExpectedThrowable(UnsupportedOperationException.class,
484                 () -> comments.remove(0));
485         assertWithMessage("Exception message not expected")
486                 .that(ex.getClass())
487                 .isEqualTo(UnsupportedOperationException.class);
488     }
489 
490     private static FileContents createFileContents(List<String> lines) {
491         return new FileContents(new FileText(new File("filename"), lines));
492     }
493 
494     private static void reportBlockComment(FileContents fileContents, List<String> lines,
495             int startLineNo, int endLineNo) {
496         final int startColNo = lines.get(startLineNo - 1).indexOf("/*");
497         final int endColNo = lines.get(endLineNo - 1).indexOf("*/") + 1;
498         fileContents.reportBlockComment(startLineNo, startColNo, endLineNo, endColNo);
499     }
500 
501 }