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