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 testCloseStream() {
246 final XpathFileGeneratorAuditListener listener =
247 new XpathFileGeneratorAuditListener(outStream, OutputStreamOptions.CLOSE);
248 listener.finishLocalSetup();
249 listener.auditStarted(null);
250 listener.auditFinished(null);
251
252 assertWithMessage("Invalid close count")
253 .that(outStream.getCloseCount())
254 .isEqualTo(1);
255 }
256
257 @Test
258 public void testNoCloseStream() {
259 final XpathFileGeneratorAuditListener listener =
260 new XpathFileGeneratorAuditListener(outStream, OutputStreamOptions.NONE);
261 listener.finishLocalSetup();
262 listener.auditStarted(null);
263 listener.auditFinished(null);
264
265 assertWithMessage("Invalid close count")
266 .that(outStream.getCloseCount())
267 .isEqualTo(0);
268 }
269
270 @Test
271 public void testNullOutputStreamOptions() {
272 final OutputStream out = new ByteArrayOutputStream();
273 final IllegalArgumentException exception =
274 getExpectedThrowable(IllegalArgumentException.class,
275 () -> new XpathFileGeneratorAuditListener(out, null));
276 assertWithMessage("Invalid error message")
277 .that(exception.getMessage())
278 .isEqualTo("Parameter outputStreamOptions can not be null");
279 }
280
281 private AuditEvent createAuditEvent(String fileName, int lineNumber, int columnNumber,
282 Class<?> sourceClass) {
283 final Violation violation =
284 new Violation(lineNumber, columnNumber, "messages.properties", null,
285 null, null, sourceClass, null);
286
287 return new AuditEvent(this,
288 getPath(fileName), violation);
289 }
290
291 private AuditEvent createAuditEvent(String fileName, Violation violation) {
292 return new AuditEvent(this,
293 getPath(fileName), violation);
294 }
295
296 private static Violation createViolation(int lineNumber,
297 int columnNumber, int tokenType,
298 String moduleId,
299 Class<?> sourceClass) {
300 return new Violation(lineNumber, columnNumber, tokenType,
301 "messages.properties", null, null,
302 SeverityLevel.ERROR, moduleId, sourceClass, null);
303 }
304
305 private static TreeWalkerAuditEvent createTreeWalkerAuditEvent(String fileName,
306 Violation violation)
307 throws Exception {
308 final File file = new File(getPath(fileName));
309 final FileText fileText = new FileText(
310 file.getAbsoluteFile(),
311 System.getProperty("file.encoding", StandardCharsets.UTF_8.name()));
312 final FileContents fileContents = new FileContents(fileText);
313 final DetailAST rootAst = JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS);
314
315 return new TreeWalkerAuditEvent(fileContents, fileName,
316 violation, rootAst);
317 }
318
319 private static String getPath(String filename) {
320 return "src/test/resources/com/puppycrawl/tools/checkstyle/xpathfilegeneratorauditlistener/"
321 + filename;
322 }
323
324 private static void verifyOutput(String expected, AuditEvent... events) {
325 final TestByteArrayOutputStream out = new TestByteArrayOutputStream();
326
327 final XpathFileGeneratorAuditListener listener =
328 new XpathFileGeneratorAuditListener(out, OutputStreamOptions.CLOSE);
329
330 for (AuditEvent event : events) {
331 listener.addError(event);
332 }
333
334 listener.auditFinished(null);
335
336 assertWithMessage("Output stream flush count")
337 .that(out.flushCount)
338 .isEqualTo(TestUtil.adjustFlushCountForOutputStreamClose(1));
339 assertWithMessage("Output stream close count")
340 .that(out.closeCount)
341 .isEqualTo(1);
342
343 final String actual = out.toString(StandardCharsets.UTF_8);
344 assertWithMessage("Invalid suppressions file content")
345 .that(actual)
346 .isEqualTo(expected);
347 }
348
349 private static final class TestByteArrayOutputStream extends ByteArrayOutputStream {
350
351 private int closeCount;
352 private int flushCount;
353
354 @Override
355 public void close() {
356 closeCount++;
357 }
358
359 @Override
360 public void flush() {
361 flushCount++;
362 }
363
364 }
365 }