View Javadoc
1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2017 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.grammars;
21  
22  import static org.junit.Assert.assertEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertNotNull;
25  import static org.junit.Assert.assertTrue;
26  
27  import java.io.File;
28  import java.io.InputStream;
29  import java.lang.reflect.Constructor;
30  import java.lang.reflect.InvocationTargetException;
31  import java.lang.reflect.Method;
32  import java.nio.charset.StandardCharsets;
33  import java.util.Arrays;
34  
35  import org.junit.Test;
36  
37  import antlr.NoViableAltForCharException;
38  import antlr.ParserSharedInputState;
39  import antlr.SemanticException;
40  import antlr.TokenBuffer;
41  import com.puppycrawl.tools.checkstyle.AbstractTreeTestSupport;
42  import com.puppycrawl.tools.checkstyle.AstTreeStringPrinter;
43  import com.puppycrawl.tools.checkstyle.api.FileText;
44  
45  public class AstRegressionTest extends AbstractTreeTestSupport {
46      @Override
47      protected String getPackageLocation() {
48          return "com/puppycrawl/tools/checkstyle/grammars";
49      }
50  
51      @Test
52      public void testClassAstTree1() throws Exception {
53          verifyAst(getPath("InputRegressionJavaClass1Ast.txt"),
54                  getPath("InputRegressionJavaClass1.java"));
55      }
56  
57      @Test
58      public void testClassAstTree2() throws Exception {
59          verifyAst(getPath("InputRegressionJavaClass2Ast.txt"),
60                  getPath("InputRegressionJavaClass2.java"));
61      }
62  
63      @Test
64      public void testJava8ClassAstTree1() throws Exception {
65          verifyAst(getPath("InputRegressionJava8Class1Ast.txt"),
66                  getPath("InputRegressionJava8Class1.java"));
67      }
68  
69      @Test
70      public void testInterfaceAstTree1() throws Exception {
71          verifyAst(getPath("InputRegressionJavaInterface1Ast.txt"),
72                  getPath("InputRegressionJavaInterface1.java"));
73      }
74  
75      @Test
76      public void testInterfaceAstTree2() throws Exception {
77          verifyAst(getPath("InputRegressionJavaInterface2Ast.txt"),
78                  getPath("InputRegressionJavaInterface2.java"));
79      }
80  
81      @Test
82      public void testJava8InterfaceAstTree1() throws Exception {
83          verifyAst(getPath("InputRegressionJava8Interface1Ast.txt"),
84                  getPath("InputRegressionJava8Interface1.java"));
85      }
86  
87      @Test
88      public void testEnumAstTree1() throws Exception {
89          verifyAst(getPath("InputRegressionJavaEnum1Ast.txt"),
90                  getPath("InputRegressionJavaEnum1.java"));
91      }
92  
93      @Test
94      public void testEnumAstTree2() throws Exception {
95          verifyAst(getPath("InputRegressionJavaEnum2Ast.txt"),
96                  getPath("InputRegressionJavaEnum2.java"));
97      }
98  
99      @Test
100     public void testAnnotationAstTree1() throws Exception {
101         verifyAst(getPath("InputRegressionJavaAnnotation1Ast.txt"),
102                 getPath("InputRegressionJavaAnnotation1.java"));
103     }
104 
105     @Test
106     public void testUnusedConstructors1() throws Exception {
107         final Class<?> clss = GeneratedJavaLexer.class;
108         final Constructor<?> constructor = clss.getDeclaredConstructor(InputStream.class);
109 
110         assertNotNull("InputStream should not be null",
111                 constructor.newInstance((InputStream) null));
112     }
113 
114     @Test
115     public void testUnusedConstructors2() throws Exception {
116         final Class<?> clss = GeneratedJavaRecognizer.class;
117         final Constructor<?> constructor = clss
118                 .getDeclaredConstructor(ParserSharedInputState.class);
119 
120         assertNotNull("ParserSharedInputState should not be null",
121                 constructor.newInstance((ParserSharedInputState) null));
122     }
123 
124     @Test
125     public void testUnusedConstructors3() throws Exception {
126         final Class<?> clss = GeneratedJavaRecognizer.class;
127         final Constructor<?> constructor = clss.getDeclaredConstructor(TokenBuffer.class);
128 
129         assertNotNull("TokenBuffer should not be null",
130                 constructor.newInstance((TokenBuffer) null));
131     }
132 
133     @Test
134     public void testCustomAstTree() throws Exception {
135         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "\t");
136         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "\r\n");
137         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "\n");
138         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "\r\r");
139         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "\r");
140         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "\u000c\f");
141         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "// \n",
142                 AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
143         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "// \r",
144                 AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
145         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "// \r\n",
146                 AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
147         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "/* \n */",
148                 AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
149         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "/* \r\n */",
150                 AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
151         verifyAstRaw(getPath("InputRegressionEmptyAst.txt"), "/* \r" + "\u0000\u0000" + " */",
152                 AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
153     }
154 
155     @Test
156     public void testNewlineCr() throws Exception {
157         verifyAst(getPath("InputNewlineCrAtEndOfFileAst.txt"),
158                 getPath("InputAstRegressionNewlineCrAtEndOfFile.java"),
159                 AstTreeStringPrinter.PrintOptions.WITH_COMMENTS);
160     }
161 
162     @Test
163     public void testImpossibleExceptions() throws Exception {
164         AssertGeneratedJavaLexer.verifyFail("mSTD_ESC", 'a');
165         AssertGeneratedJavaLexer.verifyFail("mSTD_ESC", '0', (char) 0xFFFF);
166         AssertGeneratedJavaLexer.verifyFail("mSTD_ESC", '4', (char) 0xFFFF);
167         AssertGeneratedJavaLexer.verifyFail("mCHAR_LITERAL", '\'', '\'');
168         AssertGeneratedJavaLexer.verifyFail("mHEX_DIGIT", ';');
169         AssertGeneratedJavaLexer.verifyFail("mEXPONENT", ';');
170         AssertGeneratedJavaLexer.verifyFail("mBINARY_DIGIT", '2');
171         AssertGeneratedJavaLexer.verifyFail("mSIGNED_INTEGER", 'a');
172         AssertGeneratedJavaLexer.verifyFail("mID_START", '%');
173         AssertGeneratedJavaLexer.verifyFail("mID_START", (char) 0xBF);
174         AssertGeneratedJavaLexer.verifyFailNoGuessing("mID_START", (char) 0xBF);
175         AssertGeneratedJavaLexer.verifyFail("mID_PART", '%');
176         AssertGeneratedJavaLexer.verifyFail("mID_PART", (char) 0xBF);
177         AssertGeneratedJavaLexer.verifyFailNoGuessing("mID_PART", (char) 0xBF);
178         AssertGeneratedJavaLexer.verifyFail("mESC", '\\', 'a');
179         AssertGeneratedJavaLexer.verifyFail("mLONG_LITERAL", '0', ';');
180         AssertGeneratedJavaLexer.verifyFail("mLONG_LITERAL", '1', ';');
181         AssertGeneratedJavaLexer.verifyFail("mLONG_LITERAL", ';');
182         AssertGeneratedJavaLexer.verifyFail("mINT_LITERAL", ';');
183         AssertGeneratedJavaLexer.verifyFail("mHEX_DOUBLE_LITERAL", '0', 'a');
184         AssertGeneratedJavaLexer.verifyFail("mHEX_FLOAT_LITERAL", '0', 'a');
185     }
186 
187     @Test
188     public void testImpossibleValid() throws Exception {
189         AssertGeneratedJavaLexer.verifyPass("mSTD_ESC", 'n');
190         AssertGeneratedJavaLexer.verifyPass("mELLIPSIS", '.', '.', '.');
191         AssertGeneratedJavaLexer.verifyPass("mDOT", '.');
192         AssertGeneratedJavaLexer.verifyPass("mBINARY_EXPONENT", 'p', '0', ';');
193         AssertGeneratedJavaLexer.verifyPass("mHEX_DIGIT", '0');
194         AssertGeneratedJavaLexer.verifyPass("mEXPONENT", 'e', '0', ';');
195         AssertGeneratedJavaLexer.verifyPass("mBINARY_DIGIT", '0');
196         AssertGeneratedJavaLexer.verifyPass("mSIGNED_INTEGER", '0', ';');
197         AssertGeneratedJavaLexer.verifyPass("mWS", ' ', ';');
198         AssertGeneratedJavaLexer.verifyPass("mID_START", '$');
199         AssertGeneratedJavaLexer.verifyPass("mID_PART", '$');
200         AssertGeneratedJavaLexer.verifyPass("mESC", '\\', '\\');
201         AssertGeneratedJavaLexer.verifyPass("mLONG_LITERAL", '1', 'L');
202         AssertGeneratedJavaLexer.verifyPass("mINT_LITERAL", '0', ';');
203         AssertGeneratedJavaLexer.verifyPass("mFLOAT_LITERAL", '0', 'f');
204         AssertGeneratedJavaLexer.verifyPass("mDOUBLE_LITERAL", '0', 'd');
205         AssertGeneratedJavaLexer.verifyPass("mHEX_FLOAT_LITERAL", '0', 'x', '2', '_', '4', '.',
206                 '4', '4', '.', '4', 'P', '4', ';');
207         AssertGeneratedJavaLexer.verifyPass("mHEX_DOUBLE_LITERAL", '0', 'x', '2', '_', '4', '.',
208                 '4', '4', '.', '4', 'P', '4', 'D', ';');
209     }
210 
211     private static void verifyAstRaw(String expectedTextPrintFileName, String actualJava)
212             throws Exception {
213         verifyAstRaw(expectedTextPrintFileName, actualJava,
214                 AstTreeStringPrinter.PrintOptions.WITHOUT_COMMENTS);
215     }
216 
217     private static void verifyAstRaw(String expectedTextPrintFileName, String actualJava,
218             AstTreeStringPrinter.PrintOptions withComments) throws Exception {
219         final File expectedFile = new File(expectedTextPrintFileName);
220         final String expectedContents = new FileText(expectedFile, System.getProperty(
221                 "file.encoding", StandardCharsets.UTF_8.name()))
222                 .getFullText().toString().replace("\r", "");
223 
224         final FileText actualFileContents = new FileText(new File(""),
225                 Arrays.asList(actualJava.split("\\n|\\r\\n?")));
226         final String actualContents = AstTreeStringPrinter.printAst(actualFileContents,
227                 withComments);
228 
229         assertEquals("Generated AST from Java code should match pre-defined AST", expectedContents,
230                 actualContents);
231     }
232 
233     private static final class AssertGeneratedJavaLexer extends GeneratedJavaLexer {
234         private int laPosition;
235         private char[] laResults;
236 
237         private AssertGeneratedJavaLexer() {
238             super((InputStream) null);
239         }
240 
241         public static void verifyFailNoGuessing(String methodName, char... laResults)
242                 throws Exception {
243             verify(methodName, false, 0, laResults);
244         }
245 
246         public static void verifyPass(String methodName, char... laResults) throws Exception {
247             verify(methodName, true, 1, laResults);
248         }
249 
250         public static void verifyFail(String methodName, char... laResults) throws Exception {
251             verify(methodName, false, 1, laResults);
252         }
253 
254         private static void verify(String methodName, boolean expectPass, int guessing,
255                 char... laResults) throws Exception {
256             final AssertGeneratedJavaLexer instance = new AssertGeneratedJavaLexer();
257             instance.laPosition = 0;
258             instance.laResults = laResults.clone();
259             instance.inputState.guessing = guessing;
260 
261             final Method method = GeneratedJavaLexer.class.getDeclaredMethod(methodName,
262                     boolean.class);
263             boolean exception;
264 
265             try {
266                 method.invoke(instance, true);
267                 exception = false;
268             }
269             catch (InvocationTargetException ex) {
270                 if (expectPass) {
271                     throw ex;
272                 }
273 
274                 final Class<?> clss = ex.getTargetException().getClass();
275                 if (clss != NoViableAltForCharException.class
276                         && clss != SemanticException.class) {
277                     throw ex;
278                 }
279                 exception = true;
280             }
281 
282             if (expectPass) {
283                 assertFalse("Call to GeneratedJavaLexer." + methodName
284                         + " resulted in an exception", exception);
285             }
286             else {
287                 assertTrue("Call to GeneratedJavaLexer." + methodName
288                         + " did not result in an exception", exception);
289             }
290         }
291 
292         @Override
293         public char LA(int i) {
294             return laResults[laPosition + i - 1];
295         }
296 
297         @Override
298         public void consume() {
299             laPosition++;
300         }
301 
302         @Override
303         public int mark() {
304             return 1;
305         }
306     }
307 }