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 testAddErrorWithAbsoluteWindowsPath() 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 =
288 new AuditEvent(this, "C:\\Users\\SomeUser\\Code\\Test.java", violation);
289 logger.fileStarted(ev);
290 logger.addError(ev);
291 logger.fileFinished(ev);
292 logger.auditFinished(null);
293 verifyContent(getPath("ExpectedSarifLoggerAbsoluteWindowsPath.sarif"), outStream);
294 }
295
296
297
298
299
300
301
302 @Test
303 public void testAddErrorWithRelativeWindowsPath() throws IOException {
304 final SarifLogger logger = new SarifLogger(outStream,
305 OutputStreamOptions.CLOSE);
306 logger.auditStarted(null);
307 final Violation violation =
308 new Violation(1, 1,
309 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
310 getClass(), "found an error");
311 final AuditEvent ev = new AuditEvent(this, ".\\Test.java", violation);
312 logger.fileStarted(ev);
313 logger.addError(ev);
314 logger.fileFinished(ev);
315 logger.auditFinished(null);
316 verifyContent(getPath("ExpectedSarifLoggerRelativeWindowsPath.sarif"), outStream);
317 }
318
319
320
321
322
323
324 @Test
325 public void testAddErrorWithQuoteInPath() throws IOException {
326 final SarifLogger logger = new SarifLogger(outStream,
327 OutputStreamOptions.CLOSE);
328 logger.auditStarted(null);
329 final Violation violation =
330 new Violation(1, 1,
331 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
332 getClass(), "found an error");
333 final AuditEvent ev = new AuditEvent(this, "Test\".java", violation);
334 logger.fileStarted(ev);
335 logger.addError(ev);
336 logger.fileFinished(ev);
337 logger.auditFinished(null);
338 verifyContent(getPath("ExpectedSarifLoggerQuoteInPath.sarif"), outStream);
339 }
340
341
342
343
344 @Test
345 public void testCtorWithTwoParametersCloseStreamOptions() throws IOException {
346 final OutputStream infoStream = new ByteArrayOutputStream();
347 final SarifLogger logger = new SarifLogger(infoStream,
348 AutomaticBean.OutputStreamOptions.CLOSE);
349 final boolean closeStream = TestUtil.getInternalState(logger, "closeStream", Boolean.class);
350
351 assertWithMessage("closeStream should be true")
352 .that(closeStream)
353 .isTrue();
354 }
355
356
357
358
359 @Test
360 public void testCtorWithTwoParametersNoneStreamOptions() throws IOException {
361 final OutputStream infoStream = new ByteArrayOutputStream();
362 final SarifLogger logger = new SarifLogger(infoStream,
363 AutomaticBean.OutputStreamOptions.NONE);
364 final boolean closeStream = TestUtil.getInternalState(logger, "closeStream", Boolean.class);
365
366 assertWithMessage("closeStream should be false")
367 .that(closeStream)
368 .isFalse();
369 }
370
371
372
373
374
375
376
377 @Test
378 public void testNullOutputStreamOptions() {
379 final IllegalArgumentException exception =
380 getExpectedThrowable(IllegalArgumentException.class, () -> {
381 final SarifLogger logger =
382 new SarifLogger(outStream, (OutputStreamOptions) null);
383
384
385 assertWithMessage("Null instance")
386 .that(logger)
387 .isNotNull();
388 }, "Exception was expected");
389 assertWithMessage("Invalid error message")
390 .that(exception.getMessage())
391 .isEqualTo("Parameter outputStreamOptions can not be null");
392 }
393
394 @Test
395 public void testCloseStream() throws Exception {
396 final String inputFile = "InputSarifLoggerEmpty.java";
397 final String expectedReportFile = "ExpectedSarifLoggerEmpty.sarif";
398 final SarifLogger logger = new SarifLogger(outStream,
399 OutputStreamOptions.CLOSE);
400
401 verifyWithInlineConfigParserAndLogger(
402 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
403
404 assertWithMessage("Invalid close count")
405 .that(outStream.getCloseCount())
406 .isEqualTo(1);
407 }
408
409 @Test
410 public void testNoCloseStream() throws Exception {
411 final String inputFile = "InputSarifLoggerEmpty.java";
412 final String expectedReportFile = "ExpectedSarifLoggerEmpty.sarif";
413 final SarifLogger logger = new SarifLogger(outStream,
414 OutputStreamOptions.NONE);
415
416 verifyWithInlineConfigParserAndLogger(
417 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
418
419 assertWithMessage("Invalid close count")
420 .that(outStream.getCloseCount())
421 .isEqualTo(0);
422 assertWithMessage("Invalid flush count")
423 .that(outStream.getFlushCount())
424 .isEqualTo(1);
425 }
426
427 @Test
428 public void testFinishLocalSetup() throws Exception {
429 final String inputFile = "InputSarifLoggerEmpty.java";
430 final String expectedReportFile = "ExpectedSarifLoggerEmpty.sarif";
431 final SarifLogger logger = new SarifLogger(outStream,
432 OutputStreamOptions.CLOSE);
433
434 logger.finishLocalSetup();
435
436 verifyWithInlineConfigParserAndLogger(
437 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
438 }
439
440
441
442
443
444
445
446
447 @Test
448 public void testReadResourceWithInvalidName() {
449 final IOException exception =
450 getExpectedThrowable(IOException.class, () -> {
451 SarifLogger.readResource("random");
452 }, "Exception expected");
453 assertWithMessage("Exception message must match")
454 .that(exception.getMessage())
455 .isEqualTo("Cannot find the resource random");
456 }
457
458 @Test
459 public void testMultipleRules() throws Exception {
460 final String inputFile = "InputSarifLoggerMultipleRules.java";
461 final String expectedReportFile = "ExpectedSarifLoggerMultipleRules.sarif";
462 final SarifLogger logger = new SarifLogger(outStream,
463 OutputStreamOptions.CLOSE);
464
465 verifyWithInlineConfigParserAndLogger(
466 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
467 }
468
469 @Test
470 public void testModuleIdSuffix() throws Exception {
471 final String inputFile = "InputSarifLoggerModuleId.java";
472 final String expectedReportFile = "ExpectedSarifLoggerModuleId.sarif";
473 final SarifLogger logger = new SarifLogger(outStream,
474 OutputStreamOptions.CLOSE);
475
476 verifyWithInlineConfigParserAndLogger(
477 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
478 }
479
480 @Test
481 public void testMultipleMessageStrings() throws Exception {
482 final String inputFile = "InputSarifLoggerMultipleMessages.java";
483 final String expectedReportFile = "ExpectedSarifLoggerMultipleMessages.sarif";
484 final SarifLogger logger = new SarifLogger(outStream,
485 OutputStreamOptions.CLOSE);
486
487 verifyWithInlineConfigParserAndLogger(
488 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
489 }
490
491
492
493
494
495
496 @Test
497 public void testGetMessagesWithClassNotFoundException() throws Exception {
498 final SarifLogger logger = new SarifLogger(outStream, OutputStreamOptions.CLOSE);
499
500 final ModuleDetails fakeModule = new ModuleDetails();
501 fakeModule.setName("FakeCheck");
502 fakeModule.setFullQualifiedName("com.fake.NonExistentCheck");
503 fakeModule.setDescription("Fake check for testing");
504 fakeModule.addToViolationMessages("fake.message.key");
505
506 @SuppressWarnings("unchecked")
507 final Map<Object, ModuleDetails> ruleMetadata =
508 TestUtil.getInternalState(logger, "ruleMetadata", Map.class);
509 final Class<?> ruleKeyClass = TestUtil.getInnerClassType(SarifLogger.class, "RuleKey");
510 final Object ruleKey =
511 TestUtil.instantiate(ruleKeyClass, "com.fake.NonExistentCheck", null);
512 ruleMetadata.put(ruleKey, fakeModule);
513
514 logger.auditFinished(null);
515
516 verifyContent(getPath("ExpectedSarifLoggerClassNotFoundException.sarif"), outStream);
517 }
518
519
520
521
522
523
524 @Test
525 public void testGetMessagesWithMissingResourceException() throws Exception {
526 final SarifLogger logger = new SarifLogger(outStream, OutputStreamOptions.CLOSE);
527
528 final ModuleDetails fakeModule = new ModuleDetails();
529 fakeModule.setName("SarifLoggerTest");
530 fakeModule.setFullQualifiedName("com.puppycrawl.tools.checkstyle.SarifLoggerTest");
531 fakeModule.setDescription("Test class without resource bundle");
532 fakeModule.addToViolationMessages("nonexistent.message.key");
533
534 @SuppressWarnings("unchecked")
535 final Map<Object, ModuleDetails> ruleMetadata =
536 TestUtil.getInternalState(logger, "ruleMetadata", Map.class);
537 final Class<?> ruleKeyClass = TestUtil.getInnerClassType(SarifLogger.class, "RuleKey");
538 final Object ruleKey =
539 TestUtil.instantiate(ruleKeyClass,
540 "com.puppycrawl.tools.checkstyle.SarifLoggerTest", null);
541 ruleMetadata.put(ruleKey, fakeModule);
542
543 logger.auditFinished(null);
544
545 verifyContent(getPath("ExpectedSarifLoggerMissingResourceException.sarif"), outStream);
546 }
547
548
549
550
551
552
553
554
555
556 @Test
557 public void testRenderSeverityLevelAllLevels() throws Exception {
558 final String inputFile = "InputSarifLoggerAllSeverityLevels.java";
559 final String expectedReportFile = "ExpectedSarifLoggerAllSeverityLevels.sarif";
560 final SarifLogger logger = new SarifLogger(outStream,
561 OutputStreamOptions.CLOSE);
562
563 verifyWithInlineConfigParserAndLogger(
564 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
565 }
566
567 private static void verifyContent(
568 String expectedOutputFile,
569 ByteArrayOutputStream actualOutputStream) throws IOException {
570 final String expectedContent = readFile(expectedOutputFile);
571 final String actualContent =
572 toLfLineEnding(actualOutputStream.toString(StandardCharsets.UTF_8));
573 assertWithMessage("sarif content should match")
574 .that(actualContent)
575 .isEqualTo(expectedContent);
576 }
577
578 private static final class TestException extends RuntimeException {
579 @Serial
580 private static final long serialVersionUID = 1L;
581
582 private TestException(String msg, Throwable cause) {
583 super(msg, cause);
584 }
585
586 @Override
587 public void printStackTrace(PrintWriter printWriter) {
588 printWriter.print("stackTrace\nexample");
589 }
590 }
591 }