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