1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.OutputStream;
27 import java.io.PrintWriter;
28 import java.io.Serial;
29 import java.nio.charset.StandardCharsets;
30 import java.util.Map;
31
32 import org.junit.jupiter.api.Test;
33
34 import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
35 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
36 import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
37 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
38 import com.puppycrawl.tools.checkstyle.api.Violation;
39 import com.puppycrawl.tools.checkstyle.internal.utils.CloseAndFlushTestByteArrayOutputStream;
40 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
41 import com.puppycrawl.tools.checkstyle.meta.ModuleDetails;
42
43 public class SarifLoggerTest extends AbstractModuleTestSupport {
44
45
46
47
48
49
50 private final CloseAndFlushTestByteArrayOutputStream outStream =
51 new CloseAndFlushTestByteArrayOutputStream();
52
53 @Override
54 protected String getPackageLocation() {
55 return "com/puppycrawl/tools/checkstyle/sariflogger";
56 }
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71 public final void executeLogger(
72 SarifLoggerTest instance, SarifLogger logger, String fileName, Violation violation) {
73 final AuditEvent event = new AuditEvent(this, "Test.java", violation);
74 logger.fileStarted(event);
75 logger.addError(event);
76 logger.fileFinished(event);
77 logger.auditFinished(null);
78 }
79
80 @Test
81 public void testEscape() {
82 final String[][] encodings = {
83 {"\"", "\\\""},
84 {"\\", "\\\\"},
85 {"\b", "\\b"},
86 {"\f", "\\f"},
87 {"\n", "\\n"},
88 {"\r", "\\r"},
89 {"\t", "\\t"},
90 {"/", "\\/"},
91 {"\u0010", "\\u0010"},
92 {"\u001E", "\\u001E"},
93 {"\u001F", "\\u001F"},
94 {" ", " "},
95 {"bar1234", "bar1234"},
96 };
97 for (String[] encoding : encodings) {
98 final String encoded = SarifLogger.escape(encoding[0]);
99 assertWithMessage("\"" + encoding[0] + "\"")
100 .that(encoded)
101 .isEqualTo(encoding[1]);
102 }
103 }
104
105 @Test
106 public void testSingleError() throws Exception {
107 final String inputFile = "InputSarifLoggerSingleError.java";
108 final String expectedReportFile = "ExpectedSarifLoggerSingleError.sarif";
109 final SarifLogger logger = new SarifLogger(outStream,
110 OutputStreamOptions.CLOSE);
111
112 verifyWithInlineConfigParserAndLogger(
113 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
114 }
115
116 @Test
117 public void testAddErrorAtColumn1() throws Exception {
118 final SarifLogger logger = new SarifLogger(outStream,
119 OutputStreamOptions.CLOSE);
120 final String inputFile = "InputSarifLoggerSingleErrorColumn1.java";
121 final String expectedOutput = "ExpectedSarifLoggerSingleErrorColumn1.sarif";
122 verifyWithInlineConfigParserAndLogger(getPath(inputFile),
123 getPath(expectedOutput), logger, outStream);
124 }
125
126 @Test
127 public void testAddErrorAtColumn0() throws Exception {
128 final String inputFile = "InputSarifLoggerErrorColumn0.java";
129 final String expectedReportFile = "ExpectedSarifLoggerSingleErrorColumn0.sarif";
130 final SarifLogger logger = new SarifLogger(outStream,
131 OutputStreamOptions.CLOSE);
132
133 verifyWithInlineConfigParserAndLogger(
134 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
135 }
136
137 @Test
138 public void testAddErrorWithWarningLevel() throws Exception {
139 final SarifLogger logger = new SarifLogger(outStream,
140 OutputStreamOptions.CLOSE);
141 final String inputFile = "InputSarifLoggerSingleWarning.java";
142 final String expectedOutput = "ExpectedSarifLoggerSingleWarning.sarif";
143 verifyWithInlineConfigParserAndLogger(getPath(inputFile),
144 getPath(expectedOutput), logger, outStream);
145 }
146
147 @Test
148 public void testDoubleError() throws Exception {
149 final String inputFile = "InputSarifLoggerDoubleError.java";
150 final String expectedReportFile = "ExpectedSarifLoggerDoubleError.sarif";
151 final SarifLogger logger = new SarifLogger(outStream,
152 OutputStreamOptions.CLOSE);
153
154 verifyWithInlineConfigParserAndLogger(
155 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
156 }
157
158 @Test
159 public void testAddException() throws IOException {
160 final SarifLogger logger = new SarifLogger(outStream,
161 OutputStreamOptions.CLOSE);
162 logger.auditStarted(null);
163 final Violation message =
164 new Violation(1, 1,
165 "messages.properties", "null", null, null,
166 getClass(), "found an error");
167 final AuditEvent ev = new AuditEvent(this, null, message);
168 logger.fileStarted(ev);
169 logger.addException(ev, new TestException("msg", new RuntimeException("msg")));
170 logger.fileFinished(ev);
171 logger.auditFinished(null);
172 verifyContent(getPath("ExpectedSarifLoggerSingleException.sarif"), outStream);
173 }
174
175 @Test
176 public void testAddExceptions() throws IOException {
177 final SarifLogger logger = new SarifLogger(outStream,
178 OutputStreamOptions.CLOSE);
179 logger.auditStarted(null);
180 final Violation violation =
181 new Violation(1, 1,
182 "messages.properties", "null", null, null,
183 getClass(), "found an error");
184 final AuditEvent ev = new AuditEvent(this, null, violation);
185 final Violation violation2 =
186 new Violation(1, 1,
187 "messages.properties", "null", null, null,
188 getClass(), "found an error");
189 final AuditEvent ev2 = new AuditEvent(this, "Test.java", violation2);
190 logger.fileStarted(ev);
191 logger.addException(ev, new TestException("msg", new RuntimeException("msg")));
192 logger.fileFinished(ev);
193 logger.fileStarted(ev2);
194 logger.addException(ev2, new TestException("msg2", new RuntimeException("msg2")));
195 logger.fileFinished(ev);
196 logger.auditFinished(null);
197 verifyContent(getPath("ExpectedSarifLoggerDoubleException.sarif"), outStream);
198 }
199
200 @Test
201 public void testLineOnly() throws Exception {
202 final String inputFile = "InputSarifLoggerLineOnly.java";
203 final String expectedReportFile = "ExpectedSarifLoggerLineOnly.sarif";
204 final SarifLogger logger = new SarifLogger(outStream,
205 OutputStreamOptions.CLOSE);
206
207 verifyWithInlineConfigParserAndLogger(
208 getPath(inputFile), getPath(expectedReportFile), logger, outStream
209 );
210 }
211
212 @Test
213 public void testEmpty() throws Exception {
214 final String inputFile = "InputSarifLoggerEmpty.java";
215 final String expectedReportFile = "ExpectedSarifLoggerEmpty.sarif";
216 final SarifLogger logger = new SarifLogger(outStream,
217 OutputStreamOptions.CLOSE);
218
219 verifyWithInlineConfigParserAndLogger(
220 getPath(inputFile), getPath(expectedReportFile), logger, outStream
221 );
222 }
223
224 @Test
225 public void testAddErrorWithSpaceInPath() throws IOException {
226 final SarifLogger logger = new SarifLogger(outStream,
227 OutputStreamOptions.CLOSE);
228 logger.auditStarted(null);
229 final Violation violation =
230 new Violation(1, 1,
231 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
232 getClass(), "found an error");
233 final AuditEvent ev = new AuditEvent(this, "/home/someuser/Code/Test 2.java", violation);
234 logger.fileStarted(ev);
235 logger.addError(ev);
236 logger.fileFinished(ev);
237 logger.auditFinished(null);
238 verifyContent(getPath("ExpectedSarifLoggerSpaceInPath.sarif"), outStream);
239 }
240
241 @Test
242 public void testAddErrorWithAbsoluteLinuxPath() throws IOException {
243 final SarifLogger logger = new SarifLogger(outStream,
244 OutputStreamOptions.CLOSE);
245 logger.auditStarted(null);
246 final Violation violation =
247 new Violation(1, 1,
248 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
249 getClass(), "found an error");
250 final AuditEvent ev = new AuditEvent(this, "/home/someuser/Code/Test.java", violation);
251 logger.fileStarted(ev);
252 logger.addError(ev);
253 logger.fileFinished(ev);
254 logger.auditFinished(null);
255 verifyContent(getPath("ExpectedSarifLoggerAbsoluteLinuxPath.sarif"), outStream);
256 }
257
258 @Test
259 public void testAddErrorWithRelativeLinuxPath() throws IOException {
260 final SarifLogger logger = new SarifLogger(outStream,
261 OutputStreamOptions.CLOSE);
262 logger.auditStarted(null);
263 final Violation violation =
264 new Violation(1, 1,
265 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
266 getClass(), "found an error");
267 final AuditEvent ev = new AuditEvent(this, "./Test.java", violation);
268 logger.fileStarted(ev);
269 logger.addError(ev);
270 logger.fileFinished(ev);
271 logger.auditFinished(null);
272 verifyContent(getPath("ExpectedSarifLoggerRelativeLinuxPath.sarif"), outStream);
273 }
274
275 @Test
276 public void testAddErrorWithAbsoluteWindowsPath() throws IOException {
277 final SarifLogger logger = new SarifLogger(outStream,
278 OutputStreamOptions.CLOSE);
279 logger.auditStarted(null);
280 final Violation violation =
281 new Violation(1, 1,
282 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
283 getClass(), "found an error");
284 final AuditEvent ev =
285 new AuditEvent(this, "C:\\Users\\SomeUser\\Code\\Test.java", violation);
286 logger.fileStarted(ev);
287 logger.addError(ev);
288 logger.fileFinished(ev);
289 logger.auditFinished(null);
290 verifyContent(getPath("ExpectedSarifLoggerAbsoluteWindowsPath.sarif"), outStream);
291 }
292
293 @Test
294 public void testAddErrorWithRelativeWindowsPath() throws IOException {
295 final SarifLogger logger = new SarifLogger(outStream,
296 OutputStreamOptions.CLOSE);
297 logger.auditStarted(null);
298 final Violation violation =
299 new Violation(1, 1,
300 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
301 getClass(), "found an error");
302 final AuditEvent ev = new AuditEvent(this, ".\\Test.java", violation);
303 logger.fileStarted(ev);
304 logger.addError(ev);
305 logger.fileFinished(ev);
306 logger.auditFinished(null);
307 verifyContent(getPath("ExpectedSarifLoggerRelativeWindowsPath.sarif"), outStream);
308 }
309
310
311
312
313 @Test
314 public void testCtorWithTwoParametersCloseStreamOptions() throws IOException {
315 final OutputStream infoStream = new ByteArrayOutputStream();
316 final SarifLogger logger = new SarifLogger(infoStream,
317 AutomaticBean.OutputStreamOptions.CLOSE);
318 final boolean closeStream = TestUtil.getInternalState(logger, "closeStream", Boolean.class);
319
320 assertWithMessage("closeStream should be true")
321 .that(closeStream)
322 .isTrue();
323 }
324
325
326
327
328 @Test
329 public void testCtorWithTwoParametersNoneStreamOptions() throws IOException {
330 final OutputStream infoStream = new ByteArrayOutputStream();
331 final SarifLogger logger = new SarifLogger(infoStream,
332 AutomaticBean.OutputStreamOptions.NONE);
333 final boolean closeStream = TestUtil.getInternalState(logger, "closeStream", Boolean.class);
334
335 assertWithMessage("closeStream should be false")
336 .that(closeStream)
337 .isFalse();
338 }
339
340 @Test
341 public void testNullOutputStreamOptions() {
342 try {
343 final SarifLogger logger = new SarifLogger(outStream, (OutputStreamOptions) null);
344
345 assertWithMessage("Null instance")
346 .that(logger)
347 .isNotNull();
348 assertWithMessage("Exception was expected").fail();
349 }
350 catch (IllegalArgumentException | IOException exception) {
351 assertWithMessage("Invalid error message")
352 .that(exception.getMessage())
353 .isEqualTo("Parameter outputStreamOptions can not be null");
354 }
355 }
356
357 @Test
358 public void testCloseStream() throws Exception {
359 final String inputFile = "InputSarifLoggerEmpty.java";
360 final String expectedReportFile = "ExpectedSarifLoggerEmpty.sarif";
361 final SarifLogger logger = new SarifLogger(outStream,
362 OutputStreamOptions.CLOSE);
363
364 verifyWithInlineConfigParserAndLogger(
365 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
366
367 assertWithMessage("Invalid close count")
368 .that(outStream.getCloseCount())
369 .isEqualTo(1);
370 }
371
372 @Test
373 public void testNoCloseStream() throws IOException {
374 final SarifLogger logger = new SarifLogger(outStream,
375 OutputStreamOptions.NONE);
376 logger.auditStarted(null);
377 logger.auditFinished(null);
378
379 assertWithMessage("Invalid close count")
380 .that(outStream.getCloseCount())
381 .isEqualTo(0);
382 assertWithMessage("Invalid flush count")
383 .that(outStream.getFlushCount())
384 .isEqualTo(1);
385
386 outStream.close();
387 verifyContent(getPath("ExpectedSarifLoggerEmpty.sarif"), outStream);
388 }
389
390 @Test
391 public void testFinishLocalSetup() throws IOException {
392 final SarifLogger logger = new SarifLogger(outStream,
393 OutputStreamOptions.CLOSE);
394 logger.finishLocalSetup();
395 logger.auditStarted(null);
396 logger.auditFinished(null);
397 assertWithMessage("instance should not be null")
398 .that(logger)
399 .isNotNull();
400 }
401
402 @Test
403 public void testReadResourceWithInvalidName() {
404 try {
405 SarifLogger.readResource("random");
406 assertWithMessage("Exception expected").fail();
407 }
408 catch (IOException exception) {
409 assertWithMessage("Exception message must match")
410 .that(exception.getMessage())
411 .isEqualTo("Cannot find the resource random");
412 }
413 }
414
415 @Test
416 public void testMultipleRules() throws Exception {
417 final String inputFile = "InputSarifLoggerMultipleRules.java";
418 final String expectedReportFile = "ExpectedSarifLoggerMultipleRules.sarif";
419 final SarifLogger logger = new SarifLogger(outStream,
420 OutputStreamOptions.CLOSE);
421
422 verifyWithInlineConfigParserAndLogger(
423 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
424 }
425
426 @Test
427 public void testModuleIdSuffix() throws Exception {
428 final String inputFile = "InputSarifLoggerModuleId.java";
429 final String expectedReportFile = "ExpectedSarifLoggerModuleId.sarif";
430 final SarifLogger logger = new SarifLogger(outStream,
431 OutputStreamOptions.CLOSE);
432
433 verifyWithInlineConfigParserAndLogger(
434 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
435 }
436
437 @Test
438 public void testMultipleMessageStrings() throws Exception {
439 final String inputFile = "InputSarifLoggerMultipleMessages.java";
440 final String expectedReportFile = "ExpectedSarifLoggerMultipleMessages.sarif";
441 final SarifLogger logger = new SarifLogger(outStream,
442 OutputStreamOptions.CLOSE);
443
444 verifyWithInlineConfigParserAndLogger(
445 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
446 }
447
448 @Test
449 public void testEscapedMessageText() throws Exception {
450 final String inputFile = "InputSarifLoggerEscapedMessage.java";
451 final String expectedReportFile = "ExpectedSarifLoggerEscapedMessage.sarif";
452 final SarifLogger logger = new SarifLogger(outStream,
453 OutputStreamOptions.CLOSE);
454
455 verifyWithInlineConfigParserAndLogger(
456 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
457 }
458
459
460
461
462
463
464
465 @Test
466 public void testGetMessagesWithClassNotFoundException() throws Exception {
467 final SarifLogger logger = new SarifLogger(outStream, OutputStreamOptions.CLOSE);
468
469 final ModuleDetails fakeModule = new ModuleDetails();
470 fakeModule.setName("FakeCheck");
471 fakeModule.setFullQualifiedName("com.fake.NonExistentCheck");
472 fakeModule.setDescription("Fake check for testing");
473 fakeModule.addToViolationMessages("fake.message.key");
474
475 @SuppressWarnings("unchecked")
476 final Map<Object, ModuleDetails> ruleMetadata =
477 TestUtil.getInternalState(logger, "ruleMetadata", Map.class);
478 final Class<?> ruleKeyClass = TestUtil.getInnerClassType(SarifLogger.class, "RuleKey");
479 final Object ruleKey =
480 TestUtil.instantiate(ruleKeyClass, "com.fake.NonExistentCheck", null);
481 ruleMetadata.put(ruleKey, fakeModule);
482
483 logger.auditFinished(null);
484
485 verifyContent(getPath("ExpectedSarifLoggerClassNotFoundException.sarif"), outStream);
486 }
487
488
489
490
491
492
493
494 @Test
495 public void testGetMessagesWithMissingResourceException() throws Exception {
496 final SarifLogger logger = new SarifLogger(outStream, OutputStreamOptions.CLOSE);
497
498 final ModuleDetails fakeModule = new ModuleDetails();
499 fakeModule.setName("SarifLoggerTest");
500 fakeModule.setFullQualifiedName("com.puppycrawl.tools.checkstyle.SarifLoggerTest");
501 fakeModule.setDescription("Test class without resource bundle");
502 fakeModule.addToViolationMessages("nonexistent.message.key");
503
504 @SuppressWarnings("unchecked")
505 final Map<Object, ModuleDetails> ruleMetadata =
506 TestUtil.getInternalState(logger, "ruleMetadata", Map.class);
507 final Class<?> ruleKeyClass = TestUtil.getInnerClassType(SarifLogger.class, "RuleKey");
508 final Object ruleKey =
509 TestUtil.instantiate(ruleKeyClass,
510 "com.puppycrawl.tools.checkstyle.SarifLoggerTest", null);
511 ruleMetadata.put(ruleKey, fakeModule);
512
513 logger.auditFinished(null);
514
515 verifyContent(getPath("ExpectedSarifLoggerMissingResourceException.sarif"), outStream);
516 }
517
518 private static void verifyContent(
519 String expectedOutputFile,
520 ByteArrayOutputStream actualOutputStream) throws IOException {
521 final String expectedContent = readFile(expectedOutputFile);
522 final String actualContent =
523 toLfLineEnding(actualOutputStream.toString(StandardCharsets.UTF_8));
524 assertWithMessage("sarif content should match")
525 .that(actualContent)
526 .isEqualTo(expectedContent);
527 }
528
529 private static final class TestException extends RuntimeException {
530 @Serial
531 private static final long serialVersionUID = 1L;
532
533 private TestException(String msg, Throwable cause) {
534 super(msg, cause);
535 }
536
537 @Override
538 public void printStackTrace(PrintWriter printWriter) {
539 printWriter.print("stackTrace\nexample");
540 }
541 }
542 }