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