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