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  
24  import java.io.ByteArrayOutputStream;
25  import java.io.IOException;
26  import java.io.PrintWriter;
27  import java.nio.charset.StandardCharsets;
28  
29  import org.junit.jupiter.api.Test;
30  
31  import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
32  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
33  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
34  import com.puppycrawl.tools.checkstyle.api.Violation;
35  import com.puppycrawl.tools.checkstyle.internal.utils.CloseAndFlushTestByteArrayOutputStream;
36  
37  public class SarifLoggerTest extends AbstractModuleTestSupport {
38  
39      /**
40       * Output stream to hold the test results. The IntelliJ IDEA issues the AutoCloseableResource
41       * warning here, so it needs to be suppressed. The {@code ByteArrayOutputStream} does not hold
42       * any resources that need to be released.
43       */
44      private final CloseAndFlushTestByteArrayOutputStream outStream =
45          new CloseAndFlushTestByteArrayOutputStream();
46  
47      @Override
48      protected String getPackageLocation() {
49          return "com/puppycrawl/tools/checkstyle/sariflogger";
50      }
51  
52      @Test
53      public void testEscape() {
54          final String[][] encodings = {
55              {"\"", "\\\""},
56              {"\\", "\\\\"},
57              {"\b", "\\b"},
58              {"\f", "\\f"},
59              {"\n", "\\n"},
60              {"\r", "\\r"},
61              {"\t", "\\t"},
62              {"/", "\\/"},
63              {"\u0010", "\\u0010"},
64              {"\u001E", "\\u001E"},
65              {"\u001F", "\\u001F"},
66              {" ", " "},
67              {"bar1234", "bar1234"},
68          };
69          for (String[] encoding : encodings) {
70              final String encoded = SarifLogger.escape(encoding[0]);
71              assertWithMessage("\"" + encoding[0] + "\"")
72                  .that(encoded)
73                  .isEqualTo(encoding[1]);
74          }
75      }
76  
77      @Test
78      public void testAddError() throws IOException {
79          final SarifLogger logger = new SarifLogger(outStream,
80                  OutputStreamOptions.CLOSE);
81          logger.auditStarted(null);
82          final Violation violation =
83                  new Violation(1, 1,
84                          "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
85                          getClass(), "found an error");
86          final AuditEvent ev = new AuditEvent(this, "Test.java", violation);
87          logger.fileStarted(ev);
88          logger.addError(ev);
89          logger.fileFinished(ev);
90          logger.auditFinished(null);
91          verifyContent(getPath("ExpectedSarifLoggerSingleError.sarif"), outStream);
92      }
93  
94      @Test
95      public void testAddErrorWithWarningLevel() throws IOException {
96          final SarifLogger logger = new SarifLogger(outStream,
97                  OutputStreamOptions.CLOSE);
98          logger.auditStarted(null);
99          final Violation violation =
100                 new Violation(1, 1,
101                         "messages.properties", "ruleId", null, SeverityLevel.WARNING, null,
102                         getClass(), "found an error");
103         final AuditEvent ev = new AuditEvent(this, "Test.java", violation);
104         logger.fileStarted(ev);
105         logger.addError(ev);
106         logger.fileFinished(ev);
107         logger.auditFinished(null);
108         verifyContent(getPath("ExpectedSarifLoggerSingleWarning.sarif"), outStream);
109     }
110 
111     @Test
112     public void testAddErrors() throws IOException {
113         final SarifLogger logger = new SarifLogger(outStream,
114                 OutputStreamOptions.CLOSE);
115         logger.auditStarted(null);
116         final Violation violation =
117                 new Violation(1, 1,
118                         "messages.properties", "ruleId", null, SeverityLevel.INFO, null,
119                         getClass(), "found an error");
120         final AuditEvent ev = new AuditEvent(this, "Test.java", violation);
121         final Violation violation2 =
122                 new Violation(1, 1,
123                         "messages.properties", "ruleId2", null, SeverityLevel.IGNORE, null,
124                         getClass(), "found another error");
125         final AuditEvent ev2 = new AuditEvent(this, "Test.java", violation2);
126         logger.fileStarted(ev);
127         logger.addError(ev);
128         logger.fileFinished(ev);
129         logger.fileStarted(ev2);
130         logger.addError(ev2);
131         logger.fileFinished(ev2);
132         logger.auditFinished(null);
133         verifyContent(getPath("ExpectedSarifLoggerDoubleError.sarif"), outStream);
134     }
135 
136     @Test
137     public void testAddException() throws IOException {
138         final SarifLogger logger = new SarifLogger(outStream,
139                 OutputStreamOptions.CLOSE);
140         logger.auditStarted(null);
141         final Violation message =
142                 new Violation(1, 1,
143                         "messages.properties", "null", null, null,
144                         getClass(), "found an error");
145         final AuditEvent ev = new AuditEvent(this, null, message);
146         logger.fileStarted(ev);
147         logger.addException(ev, new TestException("msg", new RuntimeException("msg")));
148         logger.fileFinished(ev);
149         logger.auditFinished(null);
150         verifyContent(getPath("ExpectedSarifLoggerSingleException.sarif"), outStream);
151     }
152 
153     @Test
154     public void testAddExceptions() throws IOException {
155         final SarifLogger logger = new SarifLogger(outStream,
156                 OutputStreamOptions.CLOSE);
157         logger.auditStarted(null);
158         final Violation violation =
159                 new Violation(1, 1,
160                         "messages.properties", "null", null, null,
161                         getClass(), "found an error");
162         final AuditEvent ev = new AuditEvent(this, null, violation);
163         final Violation violation2 =
164                 new Violation(1, 1,
165                         "messages.properties", "null", null, null,
166                         getClass(), "found an error");
167         final AuditEvent ev2 = new AuditEvent(this, "Test.java", violation2);
168         logger.fileStarted(ev);
169         logger.addException(ev, new TestException("msg", new RuntimeException("msg")));
170         logger.fileFinished(ev);
171         logger.fileStarted(ev2);
172         logger.addException(ev2, new TestException("msg2", new RuntimeException("msg2")));
173         logger.fileFinished(ev);
174         logger.auditFinished(null);
175         verifyContent(getPath("ExpectedSarifLoggerDoubleException.sarif"), outStream);
176     }
177 
178     @Test
179     public void testLineOnly() throws IOException {
180         final SarifLogger logger = new SarifLogger(outStream,
181             OutputStreamOptions.CLOSE);
182         logger.auditStarted(null);
183         final Violation violation =
184             new Violation(1, 0,
185                 "messages.properties", "ruleId", null, null,
186                 getClass(), "found an error");
187         final AuditEvent ev = new AuditEvent(this, "Test.java", violation);
188         logger.fileStarted(ev);
189         logger.addError(ev);
190         logger.fileFinished(ev);
191         logger.auditFinished(null);
192         verifyContent(getPath("ExpectedSarifLoggerLineOnly.sarif"), outStream);
193     }
194 
195     @Test
196     public void testEmpty() throws IOException {
197         final SarifLogger logger = new SarifLogger(outStream,
198                 OutputStreamOptions.CLOSE);
199         logger.auditStarted(null);
200         final Violation violation =
201                 new Violation(1, 1,
202                         "messages.properties", "null", null, null,
203                         getClass(), "found an error");
204         final AuditEvent ev = new AuditEvent(this, null, violation);
205         logger.fileStarted(ev);
206         logger.fileFinished(ev);
207         logger.auditFinished(null);
208         verifyContent(getPath("ExpectedSarifLoggerEmpty.sarif"), outStream);
209     }
210 
211     @Test
212     public void testNullOutputStreamOptions() {
213         try {
214             final SarifLogger logger = new SarifLogger(outStream, null);
215             // assert required to calm down eclipse's 'The allocated object is never used' violation
216             assertWithMessage("Null instance")
217                 .that(logger)
218                 .isNotNull();
219             assertWithMessage("Exception was expected").fail();
220         }
221         catch (IllegalArgumentException | IOException exception) {
222             assertWithMessage("Invalid error message")
223                 .that(exception.getMessage())
224                 .isEqualTo("Parameter outputStreamOptions can not be null");
225         }
226     }
227 
228     @Test
229     public void testCloseStream() throws IOException {
230         final SarifLogger logger = new SarifLogger(outStream,
231                 OutputStreamOptions.CLOSE);
232         logger.auditStarted(null);
233         logger.auditFinished(null);
234 
235         assertWithMessage("Invalid close count")
236             .that(outStream.getCloseCount())
237             .isEqualTo(1);
238 
239         verifyContent(getPath("ExpectedSarifLoggerEmpty.sarif"), outStream);
240     }
241 
242     @Test
243     public void testNoCloseStream() throws IOException {
244         final SarifLogger logger = new SarifLogger(outStream,
245                 OutputStreamOptions.NONE);
246         logger.auditStarted(null);
247         logger.auditFinished(null);
248 
249         assertWithMessage("Invalid close count")
250             .that(outStream.getCloseCount())
251             .isEqualTo(0);
252         assertWithMessage("Invalid flush count")
253             .that(outStream.getFlushCount())
254             .isEqualTo(1);
255 
256         outStream.close();
257         verifyContent(getPath("ExpectedSarifLoggerEmpty.sarif"), outStream);
258     }
259 
260     @Test
261     public void testFinishLocalSetup() throws IOException {
262         final SarifLogger logger = new SarifLogger(outStream,
263                 OutputStreamOptions.CLOSE);
264         logger.finishLocalSetup();
265         logger.auditStarted(null);
266         logger.auditFinished(null);
267         assertWithMessage("instance should not be null")
268             .that(logger)
269             .isNotNull();
270     }
271 
272     @Test
273     public void testReadResourceWithInvalidName() {
274         try {
275             SarifLogger.readResource("random");
276             assertWithMessage("Exception expected").fail();
277         }
278         catch (IOException exception) {
279             assertWithMessage("Exception message must match")
280                 .that(exception.getMessage())
281                 .isEqualTo("Cannot find the resource random");
282         }
283     }
284 
285     private static void verifyContent(
286             String expectedOutputFile,
287             ByteArrayOutputStream actualOutputStream) throws IOException {
288         final String expectedContent = readFile(expectedOutputFile);
289         final String actualContent =
290                 toLfLineEnding(actualOutputStream.toString(StandardCharsets.UTF_8));
291         assertWithMessage("sarif content should match")
292             .that(actualContent)
293             .isEqualTo(expectedContent);
294     }
295 
296     private static final class TestException extends RuntimeException {
297 
298         private static final long serialVersionUID = 1L;
299 
300         private TestException(String msg, Throwable cause) {
301             super(msg, cause);
302         }
303 
304         @Override
305         public void printStackTrace(PrintWriter printWriter) {
306             printWriter.print("stackTrace\nexample");
307         }
308     }
309 }