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;
21  
22  import static com.google.common.truth.Truth.assertWithMessage;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assumptions.assumeFalse;
25  
26  import java.io.ByteArrayOutputStream;
27  import java.io.IOException;
28  import java.io.OutputStream;
29  import java.nio.charset.StandardCharsets;
30  import java.text.MessageFormat;
31  import java.util.Arrays;
32  import java.util.Locale;
33  import java.util.ResourceBundle;
34  
35  import org.junit.jupiter.api.AfterEach;
36  import org.junit.jupiter.api.Test;
37  
38  import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
39  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
40  import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
41  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
42  import com.puppycrawl.tools.checkstyle.api.Violation;
43  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
44  
45  public class DefaultLoggerTest extends AbstractModuleTestSupport {
46  
47      private static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
48  
49      @Override
50      protected String getPackageLocation() {
51          return "com/puppycrawl/tools/checkstyle/defaultlogger";
52      }
53  
54      @AfterEach
55      public void tearDown() {
56          ResourceBundle.clearCache();
57      }
58  
59      @Test
60      public void testException() throws Exception {
61          final String inputFile = "InputDefaultLoggerTestException.java";
62          final String expectedInfoFile = "ExpectedDefaultLoggerInfoDefaultOutput.txt";
63          final String expectedErrorFile = "ExpectedDefaultLoggerErrorsTestException.txt";
64  
65          final ByteArrayOutputStream infoStream = new ByteArrayOutputStream();
66          final ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
67          final DefaultLogger dl = new DefaultLogger(infoStream, OutputStreamOptions.CLOSE,
68                  errorStream, OutputStreamOptions.CLOSE);
69  
70          verifyWithInlineConfigParserAndDefaultLogger(
71                  getNonCompilablePath(inputFile),
72                  getPath(expectedInfoFile),
73                  getPath(expectedErrorFile),
74                  dl, infoStream, errorStream);
75      }
76  
77      @Test
78      public void testSingleError() throws Exception {
79          final String inputFile = "InputDefaultLoggerTestSingleError.java";
80          final String expectedInfoFile = "ExpectedDefaultLoggerInfoDefaultOutput.txt";
81          final String expectedErrorFile = "ExpectedDefaultLoggerErrorsTestSingleError.txt";
82  
83          final ByteArrayOutputStream infoStream = new ByteArrayOutputStream();
84          final ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
85          final DefaultLogger dl = new DefaultLogger(infoStream, OutputStreamOptions.CLOSE,
86                  errorStream, OutputStreamOptions.CLOSE);
87  
88          verifyWithInlineConfigParserAndDefaultLogger(
89                  getPath(inputFile),
90                  getPath(expectedInfoFile),
91                  getPath(expectedErrorFile),
92                  dl, infoStream, errorStream);
93      }
94  
95      @Test
96      public void testMultipleErrors() throws Exception {
97          final String inputFile = "InputDefaultLoggerTestMultipleErrors.java";
98          final String expectedInfoFile = "ExpectedDefaultLoggerInfoDefaultOutput.txt";
99          final String expectedErrorFile = "ExpectedDefaultLoggerErrorsTestMultipleErrors.txt";
100 
101         final ByteArrayOutputStream infoStream = new ByteArrayOutputStream();
102         final ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
103         final DefaultLogger dl = new DefaultLogger(infoStream, OutputStreamOptions.CLOSE,
104                 errorStream, OutputStreamOptions.CLOSE);
105 
106         verifyWithInlineConfigParserAndDefaultLogger(
107                 getPath(inputFile),
108                 getPath(expectedInfoFile),
109                 getPath(expectedErrorFile),
110                 dl, infoStream, errorStream);
111     }
112 
113     @Test
114     public void testCtorWithTwoParametersCloseStreamOptions() throws Exception {
115         final String inputFile = "InputDefaultLoggerTestSingleError.java";
116         final String expectedOutputFile = "ExpectedDefaultLoggerOutputSingleError.txt";
117 
118         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
119         final DefaultLogger dl = new DefaultLogger(outputStream, OutputStreamOptions.CLOSE);
120 
121         verifyWithInlineConfigParserAndDefaultLogger(
122                 getPath(inputFile),
123                 getPath(expectedOutputFile),
124                 dl, outputStream);
125     }
126 
127     @Test
128     public void testCtorWithTwoParametersNoneStreamOptions() throws Exception {
129         final String inputFile = "InputDefaultLoggerTestSingleError.java";
130         final String expectedOutputFile = "ExpectedDefaultLoggerOutputSingleError.txt";
131 
132         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
133         final DefaultLogger dl = new DefaultLogger(outputStream, OutputStreamOptions.NONE);
134 
135         verifyWithInlineConfigParserAndDefaultLogger(
136                 getPath(inputFile),
137                 getPath(expectedOutputFile),
138                 dl, outputStream);
139     }
140 
141     /**
142      * We keep this test for 100% coverage. Until #12873.
143      * Test not updated because relies on deprecated AutomaticBean and verifies only correct field
144      * mapping.
145      */
146     @Test
147     public void testOldCtorWithTwoParametersCloseStreamOptions() {
148         final OutputStream infoStream = new ByteArrayOutputStream();
149         final DefaultLogger dl = new DefaultLogger(infoStream,
150                 AutomaticBean.OutputStreamOptions.CLOSE);
151         final boolean closeInfo = TestUtil.getInternalState(dl, "closeInfo");
152 
153         assertWithMessage("closeInfo should be true")
154                 .that(closeInfo)
155                 .isTrue();
156     }
157 
158     /**
159      * We keep this test for 100% coverage. Until #12873.
160      * Test not updated because relies on deprecated AutomaticBean and verifies only correct field
161      * mapping.
162      */
163     @Test
164     public void testOldCtorWithTwoParametersNoneStreamOptions() {
165         final OutputStream infoStream = new ByteArrayOutputStream();
166         final DefaultLogger dl = new DefaultLogger(infoStream,
167                 AutomaticBean.OutputStreamOptions.NONE);
168         final boolean closeInfo = TestUtil.getInternalState(dl, "closeInfo");
169 
170         assertWithMessage("closeInfo should be false")
171                 .that(closeInfo)
172                 .isFalse();
173     }
174 
175     @Test
176     public void testCtorWithNullParameter() {
177         final OutputStream infoStream = new ByteArrayOutputStream();
178         final DefaultLogger dl = new DefaultLogger(infoStream, OutputStreamOptions.CLOSE);
179         dl.addException(new AuditEvent(5000), new IllegalStateException("upsss"));
180         dl.auditFinished(new AuditEvent(6000));
181         final String output = infoStream.toString();
182         assertWithMessage("Message should contain exception info")
183                 .that(output)
184                 .contains("java.lang.IllegalStateException: upsss");
185     }
186 
187     @Test
188     public void testNullInfoStreamOptions() {
189         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
190         final IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
191                 () -> new DefaultLogger(outputStream, (OutputStreamOptions) null),
192                 "IllegalArgumentException expected");
193         assertWithMessage("Invalid error message")
194                 .that(ex)
195                 .hasMessageThat()
196                         .isEqualTo("Parameter infoStreamOptions can not be null");
197     }
198 
199     @Test
200     public void testNullErrorStreamOptions() {
201         final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
202         final IllegalArgumentException ex = assertThrows(IllegalArgumentException.class,
203                 () -> {
204                     final DefaultLogger defaultLogger = new DefaultLogger(outputStream,
205                             OutputStreamOptions.CLOSE, outputStream, null);
206 
207                     // Workaround for Eclipse error "The allocated object is never used"
208                     assertWithMessage("defaultLogger should be non-null")
209                             .that(defaultLogger)
210                             .isNotNull();
211                 },
212                 "IllegalArgumentException expected");
213         assertWithMessage("Invalid error message")
214                 .that(ex)
215                 .hasMessageThat()
216                         .isEqualTo("Parameter errorStreamOptions can not be null");
217     }
218 
219     @Test
220     public void testAddError() {
221         final OutputStream infoStream = new ByteArrayOutputStream();
222         final OutputStream errorStream = new ByteArrayOutputStream();
223         final String auditStartMessage = getAuditStartMessage();
224         final String auditFinishMessage = getAuditFinishMessage();
225         final DefaultLogger dl = new DefaultLogger(infoStream,
226                 OutputStreamOptions.CLOSE, errorStream,
227                 OutputStreamOptions.CLOSE);
228         dl.finishLocalSetup();
229         dl.auditStarted(null);
230         dl.addError(new AuditEvent(this, "fileName", new Violation(1, 2, "bundle", "key",
231                 null, null, getClass(), "customViolation")));
232         dl.auditFinished(null);
233         assertWithMessage("expected output")
234             .that(infoStream.toString())
235             .isEqualTo(auditStartMessage
236                         + System.lineSeparator()
237                         + auditFinishMessage
238                         + System.lineSeparator());
239         assertWithMessage("expected output")
240             .that(errorStream.toString())
241             .isEqualTo("[ERROR] fileName:1:2: customViolation [DefaultLoggerTest]"
242                 + System.lineSeparator());
243     }
244 
245     @Test
246     public void testAddErrorModuleId() {
247         final OutputStream infoStream = new ByteArrayOutputStream();
248         final OutputStream errorStream = new ByteArrayOutputStream();
249         final String auditFinishMessage = getAuditFinishMessage();
250         final String auditStartMessage = getAuditStartMessage();
251         final DefaultLogger dl = new DefaultLogger(infoStream, OutputStreamOptions.CLOSE,
252                 errorStream, OutputStreamOptions.CLOSE);
253         dl.finishLocalSetup();
254         dl.auditStarted(null);
255         dl.addError(new AuditEvent(this, "fileName", new Violation(1, 2, "bundle", "key",
256                 null, "moduleId", getClass(), "customViolation")));
257         dl.auditFinished(null);
258         assertWithMessage("expected output")
259             .that(infoStream.toString())
260             .isEqualTo(auditStartMessage
261                         + System.lineSeparator()
262                         + auditFinishMessage
263                         + System.lineSeparator());
264         assertWithMessage("expected output")
265             .that(errorStream.toString())
266             .isEqualTo("[ERROR] fileName:1:2: customViolation [moduleId]"
267                 + System.lineSeparator());
268     }
269 
270     @Test
271     public void testAddErrorIgnoreSeverityLevel() {
272         final OutputStream infoStream = new ByteArrayOutputStream();
273         final OutputStream errorStream = new ByteArrayOutputStream();
274         final DefaultLogger defaultLogger = new DefaultLogger(
275             infoStream, OutputStreamOptions.CLOSE,
276             errorStream, OutputStreamOptions.CLOSE);
277         defaultLogger.finishLocalSetup();
278         defaultLogger.auditStarted(null);
279         final Violation ignorableViolation = new Violation(1, 2, "bundle", "key",
280                                                            null, SeverityLevel.IGNORE, null,
281                                                            getClass(), "customViolation");
282         defaultLogger.addError(new AuditEvent(this, "fileName", ignorableViolation));
283         defaultLogger.auditFinished(null);
284         assertWithMessage("No violation was expected")
285             .that(errorStream.toString())
286             .isEmpty();
287     }
288 
289     @Test
290     public void testFinishLocalSetup() {
291         final OutputStream infoStream = new ByteArrayOutputStream();
292         final DefaultLogger dl = new DefaultLogger(infoStream,
293                 OutputStreamOptions.CLOSE);
294         dl.finishLocalSetup();
295         dl.auditStarted(null);
296         dl.auditFinished(null);
297         assertWithMessage("instance should not be null")
298             .that(dl)
299             .isNotNull();
300     }
301 
302     /**
303      * Verifies that the language specified with the system property {@code user.language} exists.
304      */
305     @Test
306     public void testLanguageIsValid() {
307         final String language = DEFAULT_LOCALE.getLanguage();
308         assumeFalse(language.isEmpty(), "Locale not set");
309         assertWithMessage("Invalid language")
310                 .that(Arrays.asList(Locale.getISOLanguages()))
311                 .contains(language);
312     }
313 
314     /**
315      * Verifies that the country specified with the system property {@code user.country} exists.
316      */
317     @Test
318     public void testCountryIsValid() {
319         final String country = DEFAULT_LOCALE.getCountry();
320         assumeFalse(country.isEmpty(), "Locale not set");
321         assertWithMessage("Invalid country")
322                 .that(Arrays.asList(Locale.getISOCountries()))
323                 .contains(country);
324     }
325 
326     @Test
327     public void testNewCtor() throws Exception {
328         final ResourceBundle bundle = ResourceBundle.getBundle(
329                 Definitions.CHECKSTYLE_BUNDLE, Locale.ENGLISH);
330         final String auditStartedMessage = bundle.getString(DefaultLogger.AUDIT_STARTED_MESSAGE);
331         final String auditFinishedMessage = bundle.getString(DefaultLogger.AUDIT_FINISHED_MESSAGE);
332         final String addExceptionMessage = new MessageFormat(bundle.getString(
333                 DefaultLogger.ADD_EXCEPTION_MESSAGE), Locale.ENGLISH).format(new String[] {"myfile"}
334         );
335         final String infoOutput;
336         final String errorOutput;
337         try (MockByteArrayOutputStream infoStream = new MockByteArrayOutputStream()) {
338             try (MockByteArrayOutputStream errorStream = new MockByteArrayOutputStream()) {
339                 final DefaultLogger dl = new DefaultLogger(
340                         infoStream, OutputStreamOptions.CLOSE,
341                         errorStream, OutputStreamOptions.CLOSE);
342                 dl.auditStarted(null);
343                 dl.addException(new AuditEvent(5000, "myfile"),
344                         new IllegalStateException("upsss"));
345                 dl.auditFinished(new AuditEvent(6000, "myfile"));
346                 infoOutput = infoStream.toString(StandardCharsets.UTF_8);
347                 errorOutput = errorStream.toString(StandardCharsets.UTF_8);
348 
349                 assertWithMessage("Info stream should be closed")
350                         .that(infoStream.closedCount)
351                         .isGreaterThan(0);
352                 assertWithMessage("Error stream should be closed")
353                         .that(errorStream.closedCount)
354                         .isGreaterThan(0);
355             }
356         }
357         assertWithMessage("Violation should contain message `audit started`")
358                 .that(infoOutput)
359                 .contains(auditStartedMessage);
360         assertWithMessage("Violation should contain message `audit finished`")
361                 .that(infoOutput)
362                 .contains(auditFinishedMessage);
363         assertWithMessage("Violation should contain exception info")
364                 .that(errorOutput)
365                 .contains(addExceptionMessage);
366         assertWithMessage("Violation should contain exception message")
367                 .that(errorOutput)
368                 .contains("java.lang.IllegalStateException: upsss");
369     }
370 
371     @Test
372     public void testStreamsNotClosedByLogger() throws IOException {
373         try (MockByteArrayOutputStream infoStream = new MockByteArrayOutputStream();
374              MockByteArrayOutputStream errorStream = new MockByteArrayOutputStream()) {
375             final DefaultLogger defaultLogger = new DefaultLogger(
376                 infoStream, OutputStreamOptions.NONE,
377                 errorStream, OutputStreamOptions.NONE);
378             defaultLogger.auditStarted(null);
379             defaultLogger.auditFinished(null);
380             assertWithMessage("Info stream should be open")
381                 .that(infoStream.closedCount)
382                 .isEqualTo(0);
383             assertWithMessage("Error stream should be open")
384                 .that(errorStream.closedCount)
385                 .isEqualTo(0);
386         }
387     }
388 
389     private static LocalizedMessage getAuditStartMessageClass() {
390         return new LocalizedMessage(Definitions.CHECKSTYLE_BUNDLE,
391                 DefaultLogger.class, "DefaultLogger.auditStarted");
392     }
393 
394     private static LocalizedMessage getAuditFinishMessageClass() {
395         return new LocalizedMessage(Definitions.CHECKSTYLE_BUNDLE,
396                 DefaultLogger.class, "DefaultLogger.auditFinished");
397     }
398 
399     private static String getAuditStartMessage() {
400         final LocalizedMessage auditStartMessage = getAuditStartMessageClass();
401         return auditStartMessage.getMessage();
402     }
403 
404     private static String getAuditFinishMessage() {
405         final LocalizedMessage auditFinishMessage = getAuditFinishMessageClass();
406         return auditFinishMessage.getMessage();
407     }
408 
409     private static final class MockByteArrayOutputStream extends ByteArrayOutputStream {
410 
411         private int closedCount;
412 
413         @Override
414         public void close() throws IOException {
415             super.close();
416             ++closedCount;
417         }
418 
419     }
420 
421 }