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.File;
27 import java.io.OutputStream;
28 import java.nio.charset.StandardCharsets;
29
30 import org.junit.jupiter.api.Test;
31
32 import com.puppycrawl.tools.checkstyle.AbstractAutomaticBean.OutputStreamOptions;
33 import com.puppycrawl.tools.checkstyle.api.AuditEvent;
34 import com.puppycrawl.tools.checkstyle.api.DetailAST;
35 import com.puppycrawl.tools.checkstyle.api.FileContents;
36 import com.puppycrawl.tools.checkstyle.api.FileText;
37 import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
38 import com.puppycrawl.tools.checkstyle.api.TokenTypes;
39 import com.puppycrawl.tools.checkstyle.api.Violation;
40 import com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck;
41 import com.puppycrawl.tools.checkstyle.checks.coding.NestedForDepthCheck;
42 import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck;
43 import com.puppycrawl.tools.checkstyle.checks.whitespace.MethodParamPadCheck;
44 import com.puppycrawl.tools.checkstyle.internal.utils.CloseAndFlushTestByteArrayOutputStream;
45 import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
46
47 public class XpathFileGeneratorAuditListenerTest {
48
49
50 private static final String EOL = System.lineSeparator();
51
52 private static final Violation FIRST_MESSAGE = createViolation(3, 51,
53 TokenTypes.LCURLY, null, LeftCurlyCheck.class);
54
55 private static final Violation SECOND_MESSAGE = createViolation(15, 5,
56 TokenTypes.METHOD_DEF, "MyModule", MethodParamPadCheck.class);
57
58 private static final Violation THIRD_MESSAGE = createViolation(17, 13,
59 TokenTypes.LITERAL_FOR, null, NestedForDepthCheck.class);
60
61 private static final Violation FOURTH_MESSAGE = createViolation(5, 5,
62 TokenTypes.VARIABLE_DEF, "JavadocModuleId", JavadocVariableCheck.class);
63
64 private final CloseAndFlushTestByteArrayOutputStream outStream =
65 new CloseAndFlushTestByteArrayOutputStream();
66
67 static {
68 try {
69 constructEvents();
70 }
71 catch (Exception exc) {
72 throw new IllegalStateException("Failed to construct events for "
73 + FOURTH_MESSAGE.getSourceName(), exc);
74 }
75 }
76
77 private static void constructEvents() throws Exception {
78 final TreeWalkerAuditEvent event1 = createTreeWalkerAuditEvent(
79 "InputXpathFileGeneratorAuditListener.java", FIRST_MESSAGE);
80
81 final TreeWalkerAuditEvent event2 = createTreeWalkerAuditEvent(
82 "InputXpathFileGeneratorAuditListener.java", SECOND_MESSAGE);
83
84 final TreeWalkerAuditEvent event3 = createTreeWalkerAuditEvent(
85 "InputXpathFileGeneratorAuditListener.java", THIRD_MESSAGE);
86
87 final TreeWalkerAuditEvent event4 = createTreeWalkerAuditEvent(
88 "InputXpathFileGeneratorAuditListener.java", FOURTH_MESSAGE);
89
90 final XpathFileGeneratorAstFilter astFilter = new XpathFileGeneratorAstFilter();
91 astFilter.accept(event1);
92 astFilter.accept(event2);
93 astFilter.accept(event3);
94 astFilter.accept(event4);
95 }
96
97 @Test
98 public void testFinishLocalSetup() {
99 final OutputStream out = new ByteArrayOutputStream();
100 final XpathFileGeneratorAuditListener listener =
101 new XpathFileGeneratorAuditListener(out, OutputStreamOptions.CLOSE);
102
103 listener.finishLocalSetup();
104 listener.auditStarted(null);
105 listener.auditFinished(null);
106 final String actual = out.toString();
107 assertWithMessage("Output should be empty")
108 .that(actual)
109 .isEmpty();
110 }
111
112 @Test
113 public void testFileStarted() {
114 final OutputStream out = new ByteArrayOutputStream();
115 final XpathFileGeneratorAuditListener listener =
116 new XpathFileGeneratorAuditListener(out, OutputStreamOptions.CLOSE);
117 final AuditEvent ev = new AuditEvent(this, "Test.java", null);
118 listener.fileStarted(ev);
119 listener.auditFinished(null);
120 final String actual = out.toString();
121 assertWithMessage("Output should be empty")
122 .that(actual)
123 .isEmpty();
124 }
125
126 @Test
127 public void testFileFinished() {
128 final OutputStream out = new ByteArrayOutputStream();
129 final XpathFileGeneratorAuditListener listener =
130 new XpathFileGeneratorAuditListener(out, OutputStreamOptions.CLOSE);
131 final AuditEvent ev = new AuditEvent(this, "Test.java", null);
132 listener.fileFinished(ev);
133 listener.auditFinished(null);
134 final String actual = out.toString();
135 assertWithMessage("Output should be empty")
136 .that(actual)
137 .isEmpty();
138 }
139
140 @Test
141 public void testAddException() {
142 final OutputStream out = new ByteArrayOutputStream();
143 final XpathFileGeneratorAuditListener logger =
144 new XpathFileGeneratorAuditListener(out, OutputStreamOptions.CLOSE);
145 logger.auditStarted(null);
146 final Violation violation =
147 new Violation(1, 1,
148 "messages.properties", null, null, null, getClass(), null);
149 final AuditEvent ev = new AuditEvent(this, "Test.java", violation);
150
151 final UnsupportedOperationException exception =
152 getExpectedThrowable(UnsupportedOperationException.class,
153 () -> logger.addException(ev, null));
154 assertWithMessage("Invalid exception message")
155 .that(exception.getMessage())
156 .isEqualTo("Operation is not supported");
157 }
158
159 @Test
160 public void testCorrectOne() {
161 final AuditEvent event = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
162 FIRST_MESSAGE);
163
164 final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
165 + "<!DOCTYPE suppressions PUBLIC" + EOL
166 + " \"-//Checkstyle//DTD SuppressionXpathFilter Configuration 1.2"
167 + "//EN\"" + EOL
168 + " \"https://checkstyle.org/dtds/suppressions_1_2_xpath.dtd\">"
169 + EOL
170 + "<suppressions>" + EOL
171 + " <suppress-xpath" + EOL
172 + " files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
173 + " checks=\"LeftCurlyCheck\""
174 + EOL
175 + " query=\"/COMPILATION_UNIT/CLASS_DEF[./IDENT"
176 + "[@text='InputXpathFileGeneratorAuditListener']]"
177 + "/OBJBLOCK/LCURLY\"/>" + EOL
178 + "</suppressions>" + EOL;
179
180 verifyOutput(expected, event);
181 }
182
183 @Test
184 public void testCorrectTwo() {
185 final AuditEvent event1 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
186 SECOND_MESSAGE);
187
188 final AuditEvent event2 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
189 THIRD_MESSAGE);
190
191 final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
192 + "<!DOCTYPE suppressions PUBLIC" + EOL
193 + " \"-//Checkstyle//DTD SuppressionXpathFilter Configuration 1.2"
194 + "//EN\"" + EOL
195 + " \"https://checkstyle.org/dtds/suppressions_1_2_xpath.dtd\">"
196 + EOL
197 + "<suppressions>" + EOL
198 + " <suppress-xpath" + EOL
199 + " files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
200 + " id=\"MyModule\"" + EOL
201 + " query=\"/COMPILATION_UNIT/CLASS_DEF"
202 + "[./IDENT[@text='InputXpathFileGeneratorAuditListener']]"
203 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='sort']]\"/>" + EOL
204 + " <suppress-xpath" + EOL
205 + " files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
206 + " checks=\"NestedForDepthCheck\"" + EOL
207 + " query=\"/COMPILATION_UNIT/CLASS_DEF"
208 + "[./IDENT[@text='InputXpathFileGeneratorAuditListener']]"
209 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='sort']]/SLIST/LITERAL_FOR/SLIST"
210 + "/LITERAL_FOR\"/>" + EOL
211 + "</suppressions>" + EOL;
212
213 verifyOutput(expected, event1, event2);
214 }
215
216 @Test
217 public void testOnlyOneMatching() {
218 final AuditEvent event1 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
219 10, 5, MethodParamPadCheck.class);
220
221 final AuditEvent event2 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
222 5, 5, JavadocVariableCheck.class);
223
224 final AuditEvent event3 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
225 FOURTH_MESSAGE);
226
227 final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
228 + "<!DOCTYPE suppressions PUBLIC" + EOL
229 + " \"-//Checkstyle//DTD SuppressionXpathFilter Configuration 1.2"
230 + "//EN\"" + EOL
231 + " \"https://checkstyle.org/dtds/suppressions_1_2_xpath.dtd\">"
232 + EOL
233 + "<suppressions>" + EOL
234 + " <suppress-xpath" + EOL
235 + " files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
236 + " id=\"JavadocModuleId\"" + EOL
237 + " query=\"/COMPILATION_UNIT/CLASS_DEF"
238 + "[./IDENT[@text='InputXpathFileGeneratorAuditListener']]"
239 + "/OBJBLOCK/VARIABLE_DEF[./IDENT[@text='isValid']]\"/>" + EOL
240 + "</suppressions>" + EOL;
241
242 verifyOutput(expected, event1, event2, event3);
243 }
244
245 @Test
246 public void testFileNameWithXmlSpecialCharacter() {
247 final AuditEvent event = createAuditEvent("Input&XpathFileGenerator.java",
248 FIRST_MESSAGE);
249
250 final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
251 + "<!DOCTYPE suppressions PUBLIC" + EOL
252 + " \"-//Checkstyle//DTD SuppressionXpathFilter Configuration 1.2"
253 + "//EN\"" + EOL
254 + " \"https://checkstyle.org/dtds/suppressions_1_2_xpath.dtd\">"
255 + EOL
256 + "<suppressions>" + EOL
257 + " <suppress-xpath" + EOL
258 + " files=\"Input&XpathFileGenerator.java\"" + EOL
259 + " checks=\"LeftCurlyCheck\""
260 + EOL
261 + " query=\"/COMPILATION_UNIT/CLASS_DEF[./IDENT"
262 + "[@text='InputXpathFileGeneratorAuditListener']]"
263 + "/OBJBLOCK/LCURLY\"/>" + EOL
264 + "</suppressions>" + EOL;
265
266 verifyOutput(expected, event);
267 }
268
269 @Test
270 public void testCloseStream() {
271 final XpathFileGeneratorAuditListener listener =
272 new XpathFileGeneratorAuditListener(outStream, OutputStreamOptions.CLOSE);
273 listener.finishLocalSetup();
274 listener.auditStarted(null);
275 listener.auditFinished(null);
276
277 assertWithMessage("Invalid close count")
278 .that(outStream.getCloseCount())
279 .isEqualTo(1);
280 }
281
282 @Test
283 public void testNoCloseStream() {
284 final XpathFileGeneratorAuditListener listener =
285 new XpathFileGeneratorAuditListener(outStream, OutputStreamOptions.NONE);
286 listener.finishLocalSetup();
287 listener.auditStarted(null);
288 listener.auditFinished(null);
289
290 assertWithMessage("Invalid close count")
291 .that(outStream.getCloseCount())
292 .isEqualTo(0);
293 }
294
295 @Test
296 public void testNullOutputStreamOptions() {
297 final OutputStream out = new ByteArrayOutputStream();
298 final IllegalArgumentException exception =
299 getExpectedThrowable(IllegalArgumentException.class,
300 () -> new XpathFileGeneratorAuditListener(out, null));
301 assertWithMessage("Invalid error message")
302 .that(exception.getMessage())
303 .isEqualTo("Parameter outputStreamOptions can not be null");
304 }
305
306 private AuditEvent createAuditEvent(String fileName, Violation violation) {
307 return new AuditEvent(this,
308 getPath(fileName), violation);
309 }
310
311 private AuditEvent createAuditEvent(String fileName, int lineNumber, int columnNumber,
312 Class<?> sourceClass) {
313 final Violation violation =
314 new Violation(lineNumber, columnNumber, "messages.properties", null,
315 null, null, sourceClass, null);
316
317 return new AuditEvent(this,
318 getPath(fileName), violation);
319 }
320
321 private static Violation createViolation(int lineNumber,
322 int columnNumber, int tokenType,
323 String moduleId,
324 Class<?> sourceClass) {
325 return new Violation(lineNumber, columnNumber, tokenType,
326 "messages.properties", null, null,
327 SeverityLevel.ERROR, moduleId, sourceClass, null);
328 }
329
330 private static TreeWalkerAuditEvent createTreeWalkerAuditEvent(String fileName,
331 Violation violation)
332 throws Exception {
333 final File file = new File(getPath(fileName));
334 final FileText fileText = new FileText(
335 file.getAbsoluteFile(),
336 System.getProperty("file.encoding", StandardCharsets.UTF_8.name()));
337 final FileContents fileContents = new FileContents(fileText);
338 final DetailAST rootAst = JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS);
339
340 return new TreeWalkerAuditEvent(fileContents, fileName,
341 violation, rootAst);
342 }
343
344 private static String getPath(String filename) {
345 return "src/test/resources/com/puppycrawl/tools/checkstyle/xpathfilegeneratorauditlistener/"
346 + filename;
347 }
348
349 private static void verifyOutput(String expected, AuditEvent... events) {
350 final TestByteArrayOutputStream out = new TestByteArrayOutputStream();
351
352 final XpathFileGeneratorAuditListener listener =
353 new XpathFileGeneratorAuditListener(out, OutputStreamOptions.CLOSE);
354
355 for (AuditEvent event : events) {
356 listener.addError(event);
357 }
358
359 listener.auditFinished(null);
360
361 assertWithMessage("Output stream flush count")
362 .that(out.flushCount)
363 .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(1));
364 assertWithMessage("Output stream close count")
365 .that(out.closeCount)
366 .isEqualTo(1);
367
368 final String actual = out.toString(StandardCharsets.UTF_8);
369 assertWithMessage("Invalid suppressions file content")
370 .that(actual)
371 .isEqualTo(expected);
372 }
373
374 private static final class TestByteArrayOutputStream extends ByteArrayOutputStream {
375
376 private int closeCount;
377 private int flushCount;
378
379 @Override
380 public void close() {
381 closeCount++;
382 }
383
384 @Override
385 public void flush() {
386 flushCount++;
387 }
388
389 }
390
391 }