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