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         final String uri = "test://bad";
210         check.setHeaderFile(new URI(uri));
211 
212         final ReflectiveOperationException ex =
213                 getExpectedThrowable(ReflectiveOperationException.class,
214                         () -> TestUtil.invokeVoidMethod(check, "loadHeaderFile"));
215         assertWithMessage("Invalid exception cause message")
216             .that(ex)
217                 .hasCauseThat()
218                     .hasMessageThat()
219                     .isEqualTo("unable to load header file " + uri);
220     }
221 
222     @Test
223     public void testCacheHeaderFile() throws Exception {
224         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
225         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
226 
227         final DefaultConfiguration checkerConfig = createRootConfig(checkConfig);
228         final File cacheFile = Files.createTempFile(temporaryFolder.toPath(), "junit", null)
229                 .toFile();
230         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
231 
232         final String[] expected = {
233             "1: " + getCheckMessage(MSG_MISSING),
234         };
235 
236         // Content header is conflicting with Input inline header
237         verify(checkerConfig, getPath("InputHeader.java"), expected);
238         // One more time to use cache.
239         // Content header is conflicting with Input inline header
240         verify(checkerConfig, getPath("InputHeader.java"), expected);
241     }
242 
243     @Test
244     public void testCacheHeaderWithoutFile() throws Exception {
245         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
246         checkConfig.addProperty("header", "Test");
247 
248         final DefaultConfiguration checkerConfig = createRootConfig(checkConfig);
249         final File cacheFile = Files.createTempFile(temporaryFolder.toPath(), "junit", null)
250                 .toFile();
251         checkerConfig.addProperty("cacheFile", cacheFile.getPath());
252 
253         final String[] expected = {
254             "1: " + getCheckMessage(MSG_MISMATCH, "Test"),
255         };
256 
257         // Content header is conflicting with Input inline header
258         verify(checkerConfig, getPath("InputHeader.java"), expected);
259     }
260 
261     @Test
262     public void testIgnoreLinesSorted() throws Exception {
263         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
264         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.header"));
265         checkConfig.addProperty("ignoreLines", "4,2,3");
266         final String[] expected = CommonUtil.EMPTY_STRING_ARRAY;
267         verify(checkConfig, getPath("InputHeaderjava3.header"), expected);
268     }
269 
270     @Test
271     public void testLoadHeaderFileTwice() {
272         final HeaderCheck check = new HeaderCheck();
273         check.setHeader("Header");
274         final ReflectiveOperationException ex =
275                 getExpectedThrowable(ReflectiveOperationException.class,
276                         () -> TestUtil.invokeVoidMethod(check, "loadHeaderFile"));
277         assertWithMessage("Invalid exception cause message")
278                 .that(ex)
279                 .hasCauseThat()
280                         .hasMessageThat()
281                                 .isEqualTo("header has already been set - "
282                                     + "set either header or headerFile, not both");
283     }
284 
285     @Test
286     public void testHeaderIsValidWithBlankLines() throws Exception {
287         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
288         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.blank-lines.header"));
289         // Content header is conflicting with Input inline header
290         verify(checkConfig, getPath("InputHeaderBlankLines.java"));
291     }
292 
293     @Test
294     public void testHeaderIsValidWithBlankLinesBlockStyle() throws Exception {
295         final DefaultConfiguration checkConfig = createModuleConfig(HeaderCheck.class);
296         checkConfig.addProperty("headerFile", getPath("InputHeaderjava.blank-lines2.header"));
297         // Content header is conflicting with Input inline header
298         verify(checkConfig, getPath("InputHeaderBlankLines2.java"));
299     }
300 
301     @Test
302     public void testExternalResource() throws Exception {
303         final HeaderCheck check = new HeaderCheck();
304         final URI uri = CommonUtil.getUriByFilename(getPath("InputHeaderjava.header"));
305         check.setHeaderFile(uri);
306         final Set<String> results = check.getExternalResourceLocations();
307         assertWithMessage("Invalid result size")
308             .that(results.size())
309             .isEqualTo(1);
310         assertWithMessage("Invalid resource location")
311             .that(results.iterator().next())
312             .isEqualTo(uri.toASCIIString());
313     }
314 
315     @Test
316     public void testIoExceptionWhenLoadingHeader() {
317         final HeaderCheck check = new HeaderCheck();
318         try (MockedConstruction<LineNumberReader> mocked = mockConstruction(
319                 LineNumberReader.class, (mock, context) -> {
320                     when(mock.readLine()).thenThrow(IOException.class);
321                 })) {
322             final IllegalArgumentException ex =
323                     getExpectedThrowable(IllegalArgumentException.class,
324                             () -> check.setHeader("header"));
325             assertWithMessage("Invalid exception cause")
326                     .that(ex)
327                     .hasCauseThat()
328                             .isInstanceOf(IOException.class);
329             assertWithMessage("Invalid exception message")
330                     .that(ex)
331                     .hasMessageThat()
332                             .isEqualTo("unable to load header");
333         }
334     }
335 
336 }