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