View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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.checks.header;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck.MSG_MISMATCH;
24  import static com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck.MSG_MISSING;
25  import static com.puppycrawl.tools.checkstyle.internal.utils.TestUtil.getExpectedThrowable;
26  import static org.mockito.Mockito.mockConstruction;
27  import static org.mockito.Mockito.when;
28  
29  import java.io.File;
30  import java.io.IOException;
31  import java.io.LineNumberReader;
32  import java.net.URI;
33  import java.nio.file.Files;
34  import java.util.Set;
35  
36  import org.junit.jupiter.api.Test;
37  import org.junit.jupiter.api.io.TempDir;
38  import org.mockito.MockedConstruction;
39  
40  import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport;
41  import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
42  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
43  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
44  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
45  
46  public class HeaderCheckTest extends AbstractModuleTestSupport {
47  
48      @TempDir
49      public File temporaryFolder;
50  
51      @Override
52      protected String getPackageLocation() {
53          return "com/puppycrawl/tools/checkstyle/checks/header/header";
54      }
55  
56      @Test
57      public void testStaticHeader() throws Exception {
58          final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
59          checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
60          checkConfig.addProperty("ignoreLines", "");
61          final String[] expected = {
62              "1: " + getCheckMessage(MSG_MISSING),
63          };
64          // Content header is conflicting with Input inline header
65          verify(checkConfig, getPath("InputHeader.java"), expected);
66      }
67  
68      @Test
69      public void testNoHeader() throws Exception {
70          final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
71  
72          createChecker(checkConfig);
73          final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
74          // Content header is conflicting with Input inline header
75          verify(checkConfig, getPath("InputHeaderRegexp.java"), expected);
76      }
77  
78      @Test
79      public void testWhitespaceHeader() throws Exception {
80          final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
81          checkConfig.addProperty("header", "\n    \n");
82  
83          createChecker(checkConfig);
84          final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
85          // Content header is conflicting with Input inline header
86          verify(checkConfig, getPath("InputHeaderRegexp.java"), expected);
87      }
88  
89      @Test
90      public void testNonExistentHeaderFile() throws Exception {
91          final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
92          checkConfig.addProperty("headerFile", getPath("nonExistent.file"));
93          final CheckstyleException ex = getExpectedThrowable(CheckstyleException.class,
94                  () -> createChecker(checkConfig));
95          assertWithMessage("Invalid exception message")
96                  .that(ex)
97                  .hasMessageThat()
98                          .startsWith("cannot initialize module"
99                              + " com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"
100                             + " - illegal value ");
101         assertWithMessage("Invalid cause exception message")
102                 .that(ex)
103                 .hasCauseThat()
104                 .hasCauseThat()
105                 .hasCauseThat()
106                 .hasMessageThat()
107                         .startsWith("Unable to find: ");
108     }
109 
110     @Test
111     public void testInvalidCharset() throws Exception {
112         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
113         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
114         checkConfig.addProperty("charset", "XSO-8859-1");
115         final CheckstyleException ex = getExpectedThrowable(CheckstyleException.class,
116                 () -> createChecker(checkConfig));
117         assertWithMessage("Invalid exception message")
118                 .that(ex)
119                 .hasMessageThat()
120                         .isEqualTo("cannot initialize module"
121                                 + " com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"
122                                 + " - Cannot set property 'charset' to 'XSO-8859-1'");
123         assertWithMessage("Invalid cause exception message")
124                 .that(ex)
125                 .hasCauseThat()
126                 .hasCauseThat()
127                 .hasCauseThat()
128                 .hasMessageThat()
129                         .startsWith("unsupported charset: 'XSO-8859-1'");
130     }
131 
132     @Test
133     public void testEmptyFilename() {
134         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
135         checkConfig.addProperty("headerFile", "");
136         final CheckstyleException ex = getExpectedThrowable(CheckstyleException.class,
137                 () -> createChecker(checkConfig));
138         assertWithMessage("Invalid exception message")
139                 .that(ex)
140                 .hasMessageThat()
141                         .isEqualTo("cannot initialize module"
142                                 + " com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"
143                                 + " - Cannot set property 'headerFile' to ''");
144         assertWithMessage("Invalid cause exception message")
145                 .that(ex)
146                 .hasCauseThat()
147                 .hasCauseThat()
148                 .hasCauseThat()
149                 .hasMessageThat()
150                         .isEqualTo("property 'headerFile' is missing or invalid in module"
151                                 + " com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck");
152     }
153 
154     @Test
155     public void testNullFilename() {
156         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
157         checkConfig.addProperty("headerFile", null);
158         final CheckstyleException ex = getExpectedThrowable(CheckstyleException.class,
159                 () -> createChecker(checkConfig));
160         assertWithMessage("Invalid exception message")
161                 .that(ex)
162                 .hasMessageThat()
163                         .isEqualTo("cannot initialize module"
164                                 + " com.puppycrawl.tools.checkstyle.checks.header.HeaderCheck"
165                                 + " - Cannot set property 'headerFile' to 'null'");
166     }
167 
168     @Test
169     public void testNotMatch() throws Exception {
170         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
171         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
172         checkConfig.addProperty("ignoreLines", "");
173         final String[] expected = {
174             "2: " + getCheckMessage(MSG_MISMATCH,
175                     "// checkstyle: Checks Java source code and other text files for adherence to a"
176                         + " set of rules."),
177         };
178         // Content header is conflicting with Input inline header
179         verify(checkConfig, getPath("InputHeaderjava2.header"), expected);
180     }
181 
182     @Test
183     public void testIgnore() throws Exception {
184         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
185         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
186         checkConfig.addProperty("ignoreLines", "2");
187         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
188         // Content header is conflicting with Input inline header
189         verify(checkConfig, getPath("InputHeaderjava2.header"), expected);
190     }
191 
192     @Test
193     public void testSetHeaderTwice() {
194         final HeaderCheck check = new HeaderCheck();
195         check.setHeader("Header");
196         final IllegalArgumentException ex =
197                 getExpectedThrowable(IllegalArgumentException.class,
198                         () -> check.setHeader("Header2"));
199         assertWithMessage("Invalid exception message")
200                 .that(ex)
201                 .hasMessageThat()
202                         .isEqualTo("header has already been set - "
203                                 + "set either header or headerFile, not both");
204     }
205 
206     @Test
207     public void testIoExceptionWhenLoadingHeaderFile() throws Exception {
208         final HeaderCheck check = new HeaderCheck();
209         check.setHeaderFile(new URI("test://bad"));
210 
211         final ReflectiveOperationException ex =
212                 getExpectedThrowable(ReflectiveOperationException.class,
213                         () -> TestUtil.invokeMethod(check, "loadHeaderFile"));
214         assertWithMessage("Invalid exception cause message")
215             .that(ex)
216                 .hasCauseThat()
217                     .hasMessageThat()
218                     .startsWith("unable to load header file ");
219     }
220 
221     @Test
222     public void testCacheHeaderFile() throws Exception {
223         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
224         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
225 
226         final DefaultConfiguration checkerConfig = createRootConfig(checkConfig);
227         final File cacheFile = Files.createTempFile(temporaryFolder.toPath(), "junit", null)
228                 .toFile();
229         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
230 
231         final String[] expected = {
232             "1: " + getCheckMessage(MSG_MISSING),
233         };
234 
235         // Content header is conflicting with Input inline header
236         verify(checkerConfig, getPath("InputHeader.java"), expected);
237         // One more time to use cache.
238         // Content header is conflicting with Input inline header
239         verify(checkerConfig, getPath("InputHeader.java"), expected);
240     }
241 
242     @Test
243     public void testCacheHeaderWithoutFile() throws Exception {
244         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
245         checkConfig.addProperty("header", "Test");
246 
247         final DefaultConfiguration checkerConfig = createRootConfig(checkConfig);
248         final File cacheFile = Files.createTempFile(temporaryFolder.toPath(), "junit", null)
249                 .toFile();
250         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
251 
252         final String[] expected = {
253             "1: " + getCheckMessage(MSG_MISMATCH, "Test"),
254         };
255 
256         // Content header is conflicting with Input inline header
257         verify(checkerConfig, getPath("InputHeader.java"), expected);
258     }
259 
260     @Test
261     public void testIgnoreLinesSorted() throws Exception {
262         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
263         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
264         checkConfig.addProperty("ignoreLines", "4,2,3");
265         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
266         verify(checkConfig, getPath("InputHeaderjava3.header"), expected);
267     }
268 
269     @Test
270     public void testLoadHeaderFileTwice() {
271         final HeaderCheck check = new HeaderCheck();
272         check.setHeader("Header");
273         final ReflectiveOperationException ex =
274                 getExpectedThrowable(ReflectiveOperationException.class,
275                         () -> TestUtil.invokeMethod(check, "loadHeaderFile"));
276         assertWithMessage("Invalid exception cause message")
277                 .that(ex)
278                 .hasCauseThat()
279                         .hasMessageThat()
280                                 .isEqualTo("header has already been set - "
281                                     + "set either header or headerFile, not both");
282     }
283 
284     @Test
285     public void testHeaderIsValidWithBlankLines() throws Exception {
286         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
287         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.blank-lines.header"));
288         // Content header is conflicting with Input inline header
289         verify(checkConfig, getPath("InputHeaderBlankLines.java"));
290     }
291 
292     @Test
293     public void testHeaderIsValidWithBlankLinesBlockStyle() throws Exception {
294         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
295         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.blank-lines2.header"));
296         // Content header is conflicting with Input inline header
297         verify(checkConfig, getPath("InputHeaderBlankLines2.java"));
298     }
299 
300     @Test
301     public void testExternalResource() throws Exception {
302         final HeaderCheck check = new HeaderCheck();
303         final URI uri = CommonUtil.getUriByFilename(getPath("InputHeaderjava.header"));
304         check.setHeaderFile(uri);
305         final Set<String> results = check.getExternalResourceLocations();
306         assertWithMessage("Invalid result size")
307             .that(results.size())
308             .isEqualTo(1);
309         assertWithMessage("Invalid resource location")
310             .that(results.iterator().next())
311             .isEqualTo(uri.toASCIIString());
312     }
313 
314     @Test
315     public void testIoExceptionWhenLoadingHeader() {
316         final HeaderCheck check = new HeaderCheck();
317         try (MockedConstruction<LineNumberReader> mocked = mockConstruction(
318                 LineNumberReader.class, (mock, context) -> {
319                     when(mock.readLine()).thenThrow(IOException.class);
320                 })) {
321             final IllegalArgumentException ex =
322                     getExpectedThrowable(IllegalArgumentException.class,
323                             () -> check.setHeader("header"));
324             assertWithMessage("Invalid exception cause")
325                     .that(ex)
326                     .hasCauseThat()
327                             .isInstanceOf(IOException.class);
328             assertWithMessage("Invalid exception message")
329                     .that(ex)
330                     .hasMessageThat()
331                             .isEqualTo("unable to load header");
332         }
333     }
334 
335 }