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 public 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("\"%s\"", 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 SarifLogger logger =
150 new SarifLogger(outStream, OutputStreamOptions.CLOSE);
151
152 verifyWithInlineConfigParserAndLogger(
153 getPath("InputSarifLoggerDoubleError.java"),
154 getPath("ExpectedSarifLoggerDoubleError.sarif"),
155 logger,
156 outStream
157 );
158 }
159
160 @Test
161 public void testAddException() throws IOException {
162 final SarifLogger logger = new SarifLogger(outStream,
163 OutputStreamOptions.CLOSE);
164 logger.auditStarted(null);
165 final Violation message =
166 new Violation(1, 1,
167 "messages.properties", "null", null, null,
168 getClass(), "found an error");
169 final AuditEvent ev = new AuditEvent(this, null, message);
170 logger.fileStarted(ev);
171 logger.addException(ev, new TestException("msg", new RuntimeException("msg")));
172 logger.fileFinished(ev);
173 logger.auditFinished(null);
174 verifyContent(getPath("ExpectedSarifLoggerSingleException.sarif"), outStream);
175 }
176
177 @Test
178 public void testAddExceptions() throws IOException {
179 final SarifLogger logger = new SarifLogger(outStream,
180 OutputStreamOptions.CLOSE);
181 logger.auditStarted(null);
182 final Violation violation =
183 new Violation(1, 1,
184 "messages.properties", "null", null, null,
185 getClass(), "found an error");
186 final AuditEvent ev = new AuditEvent(this, null, violation);
187 final Violation violation2 =
188 new Violation(1, 1,
189 "messages.properties", "null", null, null,
190 getClass(), "found an error");
191 final AuditEvent ev2 = new AuditEvent(this, "Test.java", violation2);
192 logger.fileStarted(ev);
193 logger.addException(ev, new TestException("msg", new RuntimeException("msg")));
194 logger.fileFinished(ev);
195 logger.fileStarted(ev2);
196 logger.addException(ev2, new TestException("msg2", new RuntimeException("msg2")));
197 logger.fileFinished(ev);
198 logger.auditFinished(null);
199 verifyContent(getPath("ExpectedSarifLoggerDoubleException.sarif"), outStream);
200 }
201
202 @Test
203 public void testLineOnly() throws Exception {
204 final String inputFile = "InputSarifLoggerLineOnly.java";
205 final String expectedReportFile = "ExpectedSarifLoggerLineOnly.sarif";
206 final SarifLogger logger = new SarifLogger(outStream,
207 OutputStreamOptions.CLOSE);
208
209 verifyWithInlineConfigParserAndLogger(
210 getPath(inputFile), getPath(expectedReportFile), logger, outStream
211 );
212 }
213
214 @Test
215 public void testEmpty() throws Exception {
216 final String inputFile = "InputSarifLoggerEmpty.java";
217 final String expectedReportFile = "ExpectedSarifLoggerEmpty.sarif";
218 final SarifLogger logger = new SarifLogger(outStream,
219 OutputStreamOptions.CLOSE);
220
221 verifyWithInlineConfigParserAndLogger(
222 getPath(inputFile), getPath(expectedReportFile), logger, outStream
223 );
224 }
225
226 @Test
227 public void testAddErrorWithSpaceInPath() throws Exception {
228 final String inputFile = "InputSarifLogger Space.java";
229 final String expectedReportFile = "ExpectedSarifLoggerSpaceInPath.sarif";
230 final SarifLogger logger = new SarifLogger(outStream,
231 OutputStreamOptions.CLOSE);
232
233 verifyWithInlineConfigParserAndLogger(
234 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
235 }
236
237 @Test
238 public void testAddErrorWithAbsoluteLinuxPath() throws Exception {
239 final String inputFile = "InputSarifLoggerAbsoluteLinuxPath.java";
240 final String expectedReportFile = "ExpectedSarifLoggerAbsoluteLinuxPath.sarif";
241 final SarifLogger logger = new SarifLogger(outStream,
242 OutputStreamOptions.CLOSE);
243 verifyWithInlineConfigParserAndLogger(
244 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
245 }
246
247 @Test
248 public void testAddErrorWithRelativeLinuxPath() throws IOException {
249 final SarifLogger logger = new SarifLogger(outStream,
250 OutputStreamOptions.CLOSE);
251 logger.auditStarted(null);
252 final Violation violation =
253 new Violation(1, 1,
254 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
255 getClass(), "found an error");
256 final AuditEvent ev = new AuditEvent(this, "./Test.java", violation);
257 logger.fileStarted(ev);
258 logger.addError(ev);
259 logger.fileFinished(ev);
260 logger.auditFinished(null);
261 verifyContent(getPath("ExpectedSarifLoggerRelativeLinuxPath.sarif"), outStream);
262 }
263
264 @Test
265 public void testAddErrorWithAbsoluteWindowsPath() throws IOException {
266 final SarifLogger logger = new SarifLogger(outStream,
267 OutputStreamOptions.CLOSE);
268 logger.auditStarted(null);
269 final Violation violation =
270 new Violation(1, 1,
271 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
272 getClass(), "found an error");
273 final AuditEvent ev =
274 new AuditEvent(this, "C:\\Users\\SomeUser\\Code\\Test.java", violation);
275 logger.fileStarted(ev);
276 logger.addError(ev);
277 logger.fileFinished(ev);
278 logger.auditFinished(null);
279 verifyContent(getPath("ExpectedSarifLoggerAbsoluteWindowsPath.sarif"), outStream);
280 }
281
282 @Test
283 public void testAddErrorWithRelativeWindowsPath() throws IOException {
284 final SarifLogger logger = new SarifLogger(outStream,
285 OutputStreamOptions.CLOSE);
286 logger.auditStarted(null);
287 final Violation violation =
288 new Violation(1, 1,
289 "messages.properties", "ruleId", null, SeverityLevel.ERROR, null,
290 getClass(), "found an error");
291 final AuditEvent ev = new AuditEvent(this, ".\\Test.java", violation);
292 logger.fileStarted(ev);
293 logger.addError(ev);
294 logger.fileFinished(ev);
295 logger.auditFinished(null);
296 verifyContent(getPath("ExpectedSarifLoggerRelativeWindowsPath.sarif"), outStream);
297 }
298
299
300
301
302 @Test
303 public void testCtorWithTwoParametersCloseStreamOptions() throws IOException {
304 final OutputStream infoStream = new ByteArrayOutputStream();
305 final SarifLogger logger = new SarifLogger(infoStream,
306 AutomaticBean.OutputStreamOptions.CLOSE);
307 final boolean closeStream = TestUtil.getInternalState(logger, "closeStream", Boolean.class);
308
309 assertWithMessage("closeStream should be true")
310 .that(closeStream)
311 .isTrue();
312 }
313
314
315
316
317 @Test
318 public void testCtorWithTwoParametersNoneStreamOptions() throws IOException {
319 final OutputStream infoStream = new ByteArrayOutputStream();
320 final SarifLogger logger = new SarifLogger(infoStream,
321 AutomaticBean.OutputStreamOptions.NONE);
322 final boolean closeStream = TestUtil.getInternalState(logger, "closeStream", Boolean.class);
323
324 assertWithMessage("closeStream should be false")
325 .that(closeStream)
326 .isFalse();
327 }
328
329 @Test
330 public void testNullOutputStreamOptions() {
331 try {
332 final SarifLogger logger = new SarifLogger(outStream, (OutputStreamOptions) null);
333
334 assertWithMessage("Null instance")
335 .that(logger)
336 .isNotNull();
337 assertWithMessage("Exception was expected").fail();
338 }
339 catch (IllegalArgumentException | IOException exception) {
340 assertWithMessage("Invalid error message")
341 .that(exception.getMessage())
342 .isEqualTo("Parameter outputStreamOptions can not be null");
343 }
344 }
345
346 @Test
347 public void testCloseStream() throws Exception {
348 final String inputFile = "InputSarifLoggerEmpty.java";
349 final String expectedReportFile = "ExpectedSarifLoggerEmpty.sarif";
350 final SarifLogger logger = new SarifLogger(outStream,
351 OutputStreamOptions.CLOSE);
352
353 verifyWithInlineConfigParserAndLogger(
354 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
355
356 assertWithMessage("Invalid close count")
357 .that(outStream.getCloseCount())
358 .isEqualTo(1);
359 }
360
361 @Test
362 public void testNoCloseStream() throws IOException {
363 final SarifLogger logger = new SarifLogger(outStream,
364 OutputStreamOptions.NONE);
365 logger.auditStarted(null);
366 logger.auditFinished(null);
367
368 assertWithMessage("Invalid close count")
369 .that(outStream.getCloseCount())
370 .isEqualTo(0);
371 assertWithMessage("Invalid flush count")
372 .that(outStream.getFlushCount())
373 .isEqualTo(1);
374
375 outStream.close();
376 verifyContent(getPath("ExpectedSarifLoggerEmpty.sarif"), outStream);
377 }
378
379 @Test
380 public void testFinishLocalSetup() throws IOException {
381 final SarifLogger logger = new SarifLogger(outStream,
382 OutputStreamOptions.CLOSE);
383 logger.finishLocalSetup();
384 logger.auditStarted(null);
385 logger.auditFinished(null);
386 assertWithMessage("instance should not be null")
387 .that(logger)
388 .isNotNull();
389 }
390
391 @Test
392 public void testReadResourceWithInvalidName() {
393 try {
394 SarifLogger.readResource("random");
395 assertWithMessage("Exception expected").fail();
396 }
397 catch (IOException exception) {
398 assertWithMessage("Exception message must match")
399 .that(exception.getMessage())
400 .isEqualTo("Cannot find the resource random");
401 }
402 }
403
404 @Test
405 public void testMultipleRules() throws Exception {
406 final String inputFile = "InputSarifLoggerMultipleRules.java";
407 final String expectedReportFile = "ExpectedSarifLoggerMultipleRules.sarif";
408 final SarifLogger logger = new SarifLogger(outStream,
409 OutputStreamOptions.CLOSE);
410
411 verifyWithInlineConfigParserAndLogger(
412 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
413 }
414
415 @Test
416 public void testModuleIdSuffix() throws Exception {
417 final String inputFile = "InputSarifLoggerModuleId.java";
418 final String expectedReportFile = "ExpectedSarifLoggerModuleId.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 testMultipleMessageStrings() throws Exception {
428 final String inputFile = "InputSarifLoggerMultipleMessages.java";
429 final String expectedReportFile = "ExpectedSarifLoggerMultipleMessages.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 testEscapedMessageText() throws Exception {
439 final String inputFile = "InputSarifLoggerEscapedMessage.java";
440 final String expectedReportFile = "ExpectedSarifLoggerEscapedMessage.sarif";
441 final SarifLogger logger = new SarifLogger(outStream,
442 OutputStreamOptions.CLOSE);
443
444 verifyWithInlineConfigParserAndLogger(
445 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
446 }
447
448
449
450
451
452
453
454 @Test
455 public void testGetMessagesWithClassNotFoundException() throws Exception {
456 final SarifLogger logger = new SarifLogger(outStream, OutputStreamOptions.CLOSE);
457
458 final ModuleDetails fakeModule = new ModuleDetails();
459 fakeModule.setName("FakeCheck");
460 fakeModule.setFullQualifiedName("com.fake.NonExistentCheck");
461 fakeModule.setDescription("Fake check for testing");
462 fakeModule.addToViolationMessages("fake.message.key");
463
464 @SuppressWarnings("unchecked")
465 final Map<Object, ModuleDetails> ruleMetadata =
466 TestUtil.getInternalState(logger, "ruleMetadata", Map.class);
467 final Class<?> ruleKeyClass = TestUtil.getInnerClassType(SarifLogger.class, "RuleKey");
468 final Object ruleKey =
469 TestUtil.instantiate(ruleKeyClass, "com.fake.NonExistentCheck", null);
470 ruleMetadata.put(ruleKey, fakeModule);
471
472 logger.auditFinished(null);
473
474 verifyContent(getPath("ExpectedSarifLoggerClassNotFoundException.sarif"), outStream);
475 }
476
477
478
479
480
481
482
483 @Test
484 public void testGetMessagesWithMissingResourceException() throws Exception {
485 final SarifLogger logger = new SarifLogger(outStream, OutputStreamOptions.CLOSE);
486
487 final ModuleDetails fakeModule = new ModuleDetails();
488 fakeModule.setName("SarifLoggerTest");
489 fakeModule.setFullQualifiedName("com.puppycrawl.tools.checkstyle.SarifLoggerTest");
490 fakeModule.setDescription("Test class without resource bundle");
491 fakeModule.addToViolationMessages("nonexistent.message.key");
492
493 @SuppressWarnings("unchecked")
494 final Map<Object, ModuleDetails> ruleMetadata =
495 TestUtil.getInternalState(logger, "ruleMetadata", Map.class);
496 final Class<?> ruleKeyClass = TestUtil.getInnerClassType(SarifLogger.class, "RuleKey");
497 final Object ruleKey =
498 TestUtil.instantiate(ruleKeyClass,
499 "com.puppycrawl.tools.checkstyle.SarifLoggerTest", null);
500 ruleMetadata.put(ruleKey, fakeModule);
501
502 logger.auditFinished(null);
503
504 verifyContent(getPath("ExpectedSarifLoggerMissingResourceException.sarif"), outStream);
505 }
506
507
508
509
510
511
512
513
514
515 @Test
516 public void testRenderSeverityLevelAllLevels() throws Exception {
517 final String inputFile = "InputSarifLoggerAllSeverityLevels.java";
518 final String expectedReportFile = "ExpectedSarifLoggerAllSeverityLevels.sarif";
519 final SarifLogger logger = new SarifLogger(outStream,
520 OutputStreamOptions.CLOSE);
521
522 verifyWithInlineConfigParserAndLogger(
523 getPath(inputFile), getPath(expectedReportFile), logger, outStream);
524 }
525
526 private static void verifyContent(
527 String expectedOutputFile,
528 ByteArrayOutputStream actualOutputStream) throws IOException {
529 final String expectedContent = readFile(expectedOutputFile);
530 final String actualContent =
531 toLfLineEnding(actualOutputStream.toString(StandardCharsets.UTF_8));
532 assertWithMessage("sarif content should match")
533 .that(actualContent)
534 .isEqualTo(expectedContent);
535 }
536
537 private static final class TestException extends RuntimeException {
538 @Serial
539 private static final long serialVersionUID = 1L;
540
541 private TestException(String msg, Throwable cause) {
542 super(msg, cause);
543 }
544
545 @Override
546 public void printStackTrace(PrintWriter printWriter) {
547 printWriter.print("stackTrace\nexample");
548 }
549 }
550 }