1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.io.ByteArrayOutputStream;
27  import java.io.File;
28  import java.io.OutputStream;
29  import java.nio.charset.StandardCharsets;
30  
31  import org.junit.BeforeClass;
32  import org.junit.Test;
33  
34  import com.puppycrawl.tools.checkstyle.api.AuditEvent;
35  import com.puppycrawl.tools.checkstyle.api.AutomaticBean;
36  import com.puppycrawl.tools.checkstyle.api.DetailAST;
37  import com.puppycrawl.tools.checkstyle.api.FileContents;
38  import com.puppycrawl.tools.checkstyle.api.FileText;
39  import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
40  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
41  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
42  import com.puppycrawl.tools.checkstyle.checks.blocks.LeftCurlyCheck;
43  import com.puppycrawl.tools.checkstyle.checks.coding.NestedForDepthCheck;
44  import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocVariableCheck;
45  import com.puppycrawl.tools.checkstyle.checks.whitespace.MethodParamPadCheck;
46  import com.puppycrawl.tools.checkstyle.internal.utils.CloseAndFlushTestByteArrayOutputStream;
47  
48  public class XpathFileGeneratorAuditListenerTest {
49  
50      /** OS specific line separator. */
51      private static final String EOL = System.getProperty("line.separator");
52  
53      private static final LocalizedMessage FIRST_MESSAGE = createLocalizedMessage(3, 51,
54              TokenTypes.LCURLY, null, LeftCurlyCheck.class);
55  
56      private static final LocalizedMessage SECOND_MESSAGE = createLocalizedMessage(15, 5,
57              TokenTypes.METHOD_DEF, "MyModule", MethodParamPadCheck.class);
58  
59      private static final LocalizedMessage THIRD_MESSAGE = createLocalizedMessage(17, 13,
60              TokenTypes.LITERAL_FOR, null, NestedForDepthCheck.class);
61  
62      private static final LocalizedMessage FOURTH_MESSAGE = createLocalizedMessage(5, 5,
63              TokenTypes.VARIABLE_DEF, "JavadocModuleId", JavadocVariableCheck.class);
64  
65      private final CloseAndFlushTestByteArrayOutputStream outStream =
66              new CloseAndFlushTestByteArrayOutputStream();
67  
68      @BeforeClass
69      public static void constructEvents() throws Exception {
70          final TreeWalkerAuditEvent event1 = createTreeWalkerAuditEvent(
71                  "InputXpathFileGeneratorAuditListener.java", FIRST_MESSAGE);
72  
73          final TreeWalkerAuditEvent event2 = createTreeWalkerAuditEvent(
74                  "InputXpathFileGeneratorAuditListener.java", SECOND_MESSAGE);
75  
76          final TreeWalkerAuditEvent event3 = createTreeWalkerAuditEvent(
77                  "InputXpathFileGeneratorAuditListener.java", THIRD_MESSAGE);
78  
79          final TreeWalkerAuditEvent event4 = createTreeWalkerAuditEvent(
80                  "InputXpathFileGeneratorAuditListener.java", FOURTH_MESSAGE);
81  
82          final XpathFileGeneratorAstFilter astFilter = new XpathFileGeneratorAstFilter();
83          astFilter.accept(event1);
84          astFilter.accept(event2);
85          astFilter.accept(event3);
86          astFilter.accept(event4);
87      }
88  
89      @Test
90      public void testFinishLocalSetup() {
91          final OutputStream out = new ByteArrayOutputStream();
92          final XpathFileGeneratorAuditListener listener =
93                  new XpathFileGeneratorAuditListener(out, AutomaticBean.OutputStreamOptions.CLOSE);
94  
95          listener.finishLocalSetup();
96          listener.auditStarted(null);
97          listener.auditFinished(null);
98          final String actual = out.toString();
99          assertTrue("Output should be empty", actual.isEmpty());
100     }
101 
102     @Test
103     public void testFileStarted() {
104         final OutputStream out = new ByteArrayOutputStream();
105         final XpathFileGeneratorAuditListener listener =
106                 new XpathFileGeneratorAuditListener(out, AutomaticBean.OutputStreamOptions.CLOSE);
107         final AuditEvent ev = new AuditEvent(this, "Test.java", null);
108         listener.fileStarted(ev);
109         listener.auditFinished(null);
110         final String actual = out.toString();
111         assertTrue("Output should be empty", actual.isEmpty());
112     }
113 
114     @Test
115     public void testFileFinished() {
116         final OutputStream out = new ByteArrayOutputStream();
117         final XpathFileGeneratorAuditListener listener =
118                 new XpathFileGeneratorAuditListener(out, AutomaticBean.OutputStreamOptions.CLOSE);
119         final AuditEvent ev = new AuditEvent(this, "Test.java", null);
120         listener.fileFinished(ev);
121         listener.auditFinished(null);
122         final String actual = out.toString();
123         assertTrue("Output should be empty", actual.isEmpty());
124     }
125 
126     @Test
127     public void testAddException() {
128         final OutputStream out = new ByteArrayOutputStream();
129         final XpathFileGeneratorAuditListener logger =
130                 new XpathFileGeneratorAuditListener(out, AutomaticBean.OutputStreamOptions.CLOSE);
131         logger.auditStarted(null);
132         final LocalizedMessage message =
133                 new LocalizedMessage(1, 1,
134                         "messages.properties", null, null, null, getClass(), null);
135         final AuditEvent ev = new AuditEvent(this, "Test.java", message);
136 
137         try {
138             logger.addException(ev, null);
139             fail("Exception is excepted");
140         }
141         catch (UnsupportedOperationException ex) {
142             assertEquals("Invalid exception message",
143                     "Operation is not supported",
144                     ex.getMessage());
145         }
146     }
147 
148     @Test
149     public void testCorrectOne() {
150         final AuditEvent event = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
151                 FIRST_MESSAGE);
152 
153         final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
154                 + "<!DOCTYPE suppressions PUBLIC" + EOL
155                 + "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
156                 + "//EN\"" + EOL
157                 + "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">"
158                 + EOL
159                 + "<suppressions>" + EOL
160                 + "<suppress-xpath" + EOL
161                 + "       files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
162                 + "       checks=\"LeftCurlyCheck\""
163                 + EOL
164                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputXpathFileGeneratorAuditListener']]"
165                 + "/OBJBLOCK/LCURLY\"/>" + EOL
166                 + "</suppressions>" + EOL;
167 
168         verifyOutput(expected, event);
169     }
170 
171     @Test
172     public void testCorrectTwo() {
173         final AuditEvent event1 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
174                 SECOND_MESSAGE);
175 
176         final AuditEvent event2 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
177                 THIRD_MESSAGE);
178 
179         final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
180                 + "<!DOCTYPE suppressions PUBLIC" + EOL
181                 + "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
182                 + "//EN\"" + EOL
183                 + "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">"
184                 + EOL
185                 + "<suppressions>" + EOL
186                 + "<suppress-xpath" + EOL
187                 + "       files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
188                 + "       id=\"MyModule\"" + EOL
189                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputXpathFileGeneratorAuditListener']]"
190                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='sort']]\"/>" + EOL
191                 + "<suppress-xpath" + EOL
192                 + "       files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
193                 + "       checks=\"NestedForDepthCheck\"" + EOL
194                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputXpathFileGeneratorAuditListener']]"
195                 + "/OBJBLOCK/METHOD_DEF[./IDENT[@text='sort']]/SLIST/LITERAL_FOR/SLIST"
196                 + "/LITERAL_FOR\"/>" + EOL
197                 + "</suppressions>" + EOL;
198 
199         verifyOutput(expected, event1, event2);
200     }
201 
202     @Test
203     public void testOnlyOneMatching() {
204         final AuditEvent event1 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
205                 10, 5, MethodParamPadCheck.class);
206 
207         final AuditEvent event2 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
208                 5, 5, JavadocVariableCheck.class);
209 
210         final AuditEvent event3 = createAuditEvent("InputXpathFileGeneratorAuditListener.java",
211                 FOURTH_MESSAGE);
212 
213         final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + EOL
214                 + "<!DOCTYPE suppressions PUBLIC" + EOL
215                 + "    \"-//Checkstyle//DTD SuppressionXpathFilter Experimental Configuration 1.2"
216                 + "//EN\"" + EOL
217                 + "    \"https://checkstyle.org/dtds/suppressions_1_2_xpath_experimental.dtd\">"
218                 + EOL
219                 + "<suppressions>" + EOL
220                 + "<suppress-xpath" + EOL
221                 + "       files=\"InputXpathFileGeneratorAuditListener.java\"" + EOL
222                 + "       id=\"JavadocModuleId\"" + EOL
223                 + "       query=\"/CLASS_DEF[./IDENT[@text='InputXpathFileGeneratorAuditListener']]"
224                 + "/OBJBLOCK/VARIABLE_DEF[./IDENT[@text='isValid']]\"/>" + EOL
225                 + "</suppressions>" + EOL;
226 
227         verifyOutput(expected, event1, event2, event3);
228     }
229 
230     @Test
231     public void testCloseStream() {
232         final XpathFileGeneratorAuditListener listener =
233                 new XpathFileGeneratorAuditListener(outStream,
234                         AutomaticBean.OutputStreamOptions.CLOSE);
235         listener.finishLocalSetup();
236         listener.auditStarted(null);
237         listener.auditFinished(null);
238 
239         assertEquals("Invalid close count", 1, outStream.getCloseCount());
240     }
241 
242     @Test
243     public void testNoCloseStream() {
244         final XpathFileGeneratorAuditListener listener =
245                 new XpathFileGeneratorAuditListener(outStream,
246                         AutomaticBean.OutputStreamOptions.NONE);
247         listener.finishLocalSetup();
248         listener.auditStarted(null);
249         listener.auditFinished(null);
250 
251         assertEquals("Invalid close count", 0, outStream.getCloseCount());
252     }
253 
254     private AuditEvent createAuditEvent(String fileName, int lineNumber, int columnNumber,
255                                         Class<?> sourceClass) {
256         final LocalizedMessage message =
257                 new LocalizedMessage(lineNumber, columnNumber, "messages.properties", null,
258                         null, null, sourceClass, null);
259 
260         return new AuditEvent(this,
261                 getPath(fileName), message);
262     }
263 
264     private AuditEvent createAuditEvent(String fileName, LocalizedMessage message) {
265         return new AuditEvent(this,
266                 getPath(fileName), message);
267     }
268 
269     private static LocalizedMessage createLocalizedMessage(int lineNumber,
270                                                                    int columnNumber, int tokenType,
271                                                                    String moduleId,
272                                                                    Class<?> sourceClass) {
273         return new LocalizedMessage(lineNumber, columnNumber, tokenType,
274                 "messages.properties", null, null,
275                 SeverityLevel.ERROR, moduleId, sourceClass, null);
276     }
277 
278     private static TreeWalkerAuditEvent createTreeWalkerAuditEvent(String fileName,
279                                                                    LocalizedMessage message)
280             throws Exception {
281         final File file = new File(getPath(fileName));
282         final FileText fileText = new FileText(
283                 file.getAbsoluteFile(),
284                 System.getProperty("file.encoding", StandardCharsets.UTF_8.name()));
285         final FileContents fileContents = new FileContents(fileText);
286         final DetailAST rootAst = JavaParser.parseFile(file, JavaParser.Options.WITHOUT_COMMENTS);
287 
288         return new TreeWalkerAuditEvent(fileContents, fileName,
289                 message, rootAst);
290     }
291 
292     private static String getPath(String filename) {
293         return "src/test/resources/com/puppycrawl/tools/checkstyle/xpathfilegeneratorauditlistener/"
294                 + filename;
295     }
296 
297     private static void verifyOutput(String expected, AuditEvent... events) {
298         final TestByteArrayOutputStream out = new TestByteArrayOutputStream();
299 
300         final XpathFileGeneratorAuditListener listener =
301                 new XpathFileGeneratorAuditListener(out, AutomaticBean.OutputStreamOptions.CLOSE);
302 
303         for (AuditEvent event : events) {
304             listener.addError(event);
305         }
306 
307         listener.auditFinished(null);
308 
309         assertEquals("expected number of flushes", 1, out.flushCount);
310         assertEquals("expected number of closes", 1, out.closeCount);
311 
312         final String actual = out.toString();
313         assertEquals("Invalid suppressions file content", expected, actual);
314     }
315 
316     private static class TestByteArrayOutputStream extends ByteArrayOutputStream {
317 
318         private int closeCount;
319         private int flushCount;
320 
321         @Override
322         public void close() {
323             closeCount++;
324         }
325 
326         @Override
327         public void flush() {
328             flushCount++;
329         }
330 
331     }
332 }