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  
24  import java.io.ByteArrayOutputStream;
25  import java.io.OutputStream;
26  import java.nio.charset.StandardCharsets;
27  
28  import org.junit.jupiter.api.Test;
29  
30  import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
31  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
32  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
33  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
34  import com.puppycrawl.tools.checkstyle.api.Violation;
35  import com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck;
36  import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck;
37  import com.puppycrawl.tools.checkstyle.checks.whitespace.MethodParamPadCheck;
38  import com.puppycrawl.tools.checkstyle.internal.utils.CloseAndFlushTestByteArrayOutputStream;
39  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
40  
41  public class ChecksAndFilesSuppressionFileGeneratorAuditListenerTest {
42  
43      /** OS specific line separator. */
44      private static final String EOL = System.getProperty("line.separator");
45  
46      private static final String SUPPRESSION_XML_HEADER =
47                  "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
48                  + "<!DOCTYPE suppressions PUBLIC" + EOL
49                  + "    \"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN\"" + EOL
50                  + "    \"https://checkstyle.org/dtds/configuration_1_3.dtd\">" + EOL;
51  
52      private static final Violation FIRST_MESSAGE = createViolation(3, 51,
53              TokenTypes.LCURLY, null, LeftCurlyCheck.class);
54  
55      private static final Violation SECOND_MESSAGE = createViolation(5, 5,
56              TokenTypes.VARIABLE_DEF, "JavadocModuleId", JavadocVariableCheck.class);
57  
58      private static final Violation THIRD_MESSAGE = createViolation(7, 5,
59              TokenTypes.METHOD_DEF, "MyModule", MethodParamPadCheck.class);
60  
61      private final CloseAndFlushTestByteArrayOutputStream outStream =
62              new CloseAndFlushTestByteArrayOutputStream();
63  
64      private static Violation createViolation(int lineNumber,
65                          int columnNumber, int tokenType,
66                          String moduleId,
67                          Class<?> sourceClass) {
68  
69          return new Violation(lineNumber, columnNumber, tokenType,
70              "messages.properties", null, null,
71              SeverityLevel.ERROR, moduleId, sourceClass, null);
72      }
73  
74      /**
75       * Verifies the fileStarted method separately to give empty output
76       * which is not possible to get from the CLI run test.
77       */
78      @Test
79      public void testFileStarted() {
80          final OutputStream out = new ByteArrayOutputStream();
81          final ChecksAndFilesSuppressionFileGeneratorAuditListener listener =
82                  new ChecksAndFilesSuppressionFileGeneratorAuditListener(
83                          out, OutputStreamOptions.CLOSE);
84          final AuditEvent ev = new AuditEvent(this, "Test.java", null);
85          listener.fileStarted(ev);
86          listener.auditFinished(null);
87          final String actual = out.toString();
88          assertWithMessage("Output should be empty")
89              .that(actual)
90              .isEmpty();
91      }
92  
93      /**
94       * Verifies the fileFinished method separately to give empty output.
95       */
96      @Test
97      public void testFileFinished() {
98          final OutputStream out = new ByteArrayOutputStream();
99          final ChecksAndFilesSuppressionFileGeneratorAuditListener listener =
100                 new ChecksAndFilesSuppressionFileGeneratorAuditListener(out,
101                         OutputStreamOptions.CLOSE);
102         final AuditEvent ev = new AuditEvent(this, "Test.java", null);
103         listener.fileFinished(ev);
104         listener.auditFinished(null);
105         final String actual = out.toString();
106         assertWithMessage("Output should be empty")
107             .that(actual)
108             .isEmpty();
109     }
110 
111     /**
112      * Verifies the addException method.
113      *
114      * @throws UnsupportedOperationException this method is not supported
115      *     in {@link ChecksAndFilesSuppressionFileGeneratorAuditListener}.
116      */
117     @Test
118     public void testAddException() {
119         final OutputStream out = new ByteArrayOutputStream();
120         final ChecksAndFilesSuppressionFileGeneratorAuditListener logger =
121                 new ChecksAndFilesSuppressionFileGeneratorAuditListener(out,
122                         OutputStreamOptions.CLOSE);
123         logger.auditStarted(null);
124         final Violation violation =
125                 new Violation(1, 1,
126                         "messages.properties", null, null, null, getClass(), null);
127         final AuditEvent ev = new AuditEvent(this, "Test.java", violation);
128 
129         try {
130             logger.addException(ev, null);
131             assertWithMessage("Exception is excepted").fail();
132         }
133         catch (UnsupportedOperationException exc) {
134             assertWithMessage("Invalid exception message")
135                 .that(exc.getMessage())
136                 .isEqualTo("Operation is not supported");
137         }
138     }
139 
140     /**
141      * Verifies the close count through {@link CloseAndFlushTestByteArrayOutputStream}
142      * which is not supported by OutputStreams uses in 'Main.java'.
143      */
144     @Test
145     public void testCloseStream() {
146         final ChecksAndFilesSuppressionFileGeneratorAuditListener listener =
147                 new ChecksAndFilesSuppressionFileGeneratorAuditListener(outStream,
148                 OutputStreamOptions.CLOSE);
149         listener.finishLocalSetup();
150         listener.auditStarted(null);
151         listener.auditFinished(null);
152 
153         assertWithMessage("Invalid close count")
154             .that(outStream.getCloseCount())
155             .isEqualTo(1);
156     }
157 
158     /**
159      * Verifies the close count through {@link CloseAndFlushTestByteArrayOutputStream}.
160      */
161     @Test
162     public void testNoCloseStream() {
163         final ChecksAndFilesSuppressionFileGeneratorAuditListener listener =
164                 new ChecksAndFilesSuppressionFileGeneratorAuditListener(outStream,
165                         OutputStreamOptions.NONE);
166         listener.finishLocalSetup();
167         listener.auditStarted(null);
168         listener.auditFinished(null);
169 
170         assertWithMessage("Invalid close count")
171             .that(outStream.getCloseCount())
172             .isEqualTo(0);
173     }
174 
175     @Test
176     public void testCorrectOne() {
177         final AuditEvent event1 = createAuditEvent(
178                 "InputChecksAndFilesSuppressionFileGeneratorAuditListener.java", FIRST_MESSAGE);
179         final AuditEvent event2 = createAuditEvent(
180                 "InputChecksAndFilesSuppressionFileGeneratorAuditListener.java", SECOND_MESSAGE);
181 
182         final String expected = SUPPRESSION_XML_HEADER
183                 + "<suppressions>" + EOL
184                 + "  <suppress" + EOL
185                 + "      files=\"InputChecksAndFilesSuppressionFileGeneratorAuditListener.java\""
186                 + EOL
187                 + "      checks=\"LeftCurlyCheck\""
188                 + "/>" + EOL
189                 + "  <suppress" + EOL
190                 + "      files=\"InputChecksAndFilesSuppressionFileGeneratorAuditListener.java\""
191                 + EOL
192                 + "      id=\"JavadocModuleId\""
193                 + "/>" + EOL
194                 + "</suppressions>" + EOL;
195 
196         verifyOutput(expected, event1, event2);
197     }
198 
199     @Test
200     public void testCorrectTwo() {
201         final AuditEvent event1 = createAuditEvent(
202                 "InputChecksAndFilesSuppressionFileGeneratorAuditListener.java",
203                 5, 5, JavadocVariableCheck.class);
204 
205         final AuditEvent event2 = createAuditEvent(
206                 "InputChecksAndFilesSuppressionFileGeneratorAuditListener.java", THIRD_MESSAGE);
207 
208         final String expected = SUPPRESSION_XML_HEADER
209                 + "<suppressions>" + EOL
210                 + "  <suppress" + EOL
211                 + "      files=\"InputChecksAndFilesSuppressionFileGeneratorAuditListener.java\""
212                 + EOL
213                 + "      checks=\"JavadocVariableCheck\""
214                 + "/>" + EOL
215                 + "  <suppress" + EOL
216                 + "      files=\"InputChecksAndFilesSuppressionFileGeneratorAuditListener.java\""
217                 + EOL
218                 + "      id=\"MyModule\""
219                 + "/>" + EOL
220                 + "</suppressions>" + EOL;
221 
222         verifyOutput(expected, event1, event2);
223     }
224 
225     @Test
226     public void testFileNameNullCase() {
227         final AuditEvent event1 = new AuditEvent(this, "/", FIRST_MESSAGE);
228 
229         final String expected = SUPPRESSION_XML_HEADER
230                 + "<suppressions>" + EOL
231                 + "</suppressions>" + EOL;
232 
233         verifyOutput(expected, event1);
234     }
235 
236     /**
237      * Verifies the finishLocalSetup method separately to give empty output.
238      */
239     @Test
240     public void testFinishLocalSetup() {
241         final OutputStream out = new ByteArrayOutputStream();
242         final ChecksAndFilesSuppressionFileGeneratorAuditListener listener =
243                 new ChecksAndFilesSuppressionFileGeneratorAuditListener(out,
244                         OutputStreamOptions.CLOSE);
245 
246         listener.finishLocalSetup();
247         listener.auditStarted(null);
248         listener.auditFinished(null);
249         final String actual = out.toString();
250         assertWithMessage("Output should be empty")
251             .that(actual)
252             .isEmpty();
253     }
254 
255     private AuditEvent createAuditEvent(String fileName, int lineNumber, int columnNumber,
256                                         Class<?> sourceClass) {
257         final Violation violation =
258                 new Violation(lineNumber, columnNumber, "messages.properties", null,
259                         null, null, sourceClass, null);
260 
261         return new AuditEvent(this,
262                 getPath(fileName), violation);
263     }
264 
265     private AuditEvent createAuditEvent(String fileName, Violation violation) {
266         return new AuditEvent(this,
267                 getPath(fileName), violation);
268     }
269 
270     private static String getPath(String filename) {
271         return "src/test/resources/com/puppycrawl/tools/checkstyle/"
272                 + "checksandfilessuppressionfilegeneratorauditlistener/"
273                 + filename;
274     }
275 
276     private void verifyOutput(String expected, AuditEvent... events) {
277         final ChecksAndFilesSuppressionFileGeneratorAuditListener listener =
278             new ChecksAndFilesSuppressionFileGeneratorAuditListener(outStream,
279                 OutputStreamOptions.CLOSE);
280 
281         for (AuditEvent event : events) {
282             listener.addError(event);
283         }
284 
285         listener.auditFinished(null);
286 
287         assertWithMessage("Output stream flush count")
288             .that(outStream.getFlushCount())
289             .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(1));
290         assertWithMessage("Output stream close count")
291             .that(outStream.getCloseCount())
292             .isEqualTo(1);
293 
294         final String actual = outStream.toString(StandardCharsets.UTF_8);
295         assertWithMessage("Invalid suppressions file content")
296             .that(actual)
297             .isEqualTo(expected);
298     }
299 }