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.internal.utils;
21  
22  import static com.puppycrawl.tools.checkstyle.TreeWalker.parseWithComments;
23  
24  import java.io.File;
25  import java.io.IOException;
26  import java.lang.reflect.Constructor;
27  import java.lang.reflect.Field;
28  import java.lang.reflect.Method;
29  import java.lang.reflect.Modifier;
30  import java.nio.charset.StandardCharsets;
31  import java.util.Arrays;
32  import java.util.Optional;
33  import java.util.Set;
34  import java.util.function.Predicate;
35  
36  import antlr.ANTLRException;
37  import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
38  import com.puppycrawl.tools.checkstyle.PackageObjectFactory;
39  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
40  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
41  import com.puppycrawl.tools.checkstyle.api.DetailAST;
42  import com.puppycrawl.tools.checkstyle.api.FileContents;
43  import com.puppycrawl.tools.checkstyle.api.FileText;
44  
45  public final class TestUtil {
46  
47      private TestUtil() {
48      }
49  
50      /**
51       * Verifies that utils class has private constructor and invokes it to satisfy code coverage.
52       * @param utilClass class to test for c-tor
53       * @param checkConstructorIsPrivate flag to skip check for private visibility, it is useful
54       *                                  for Classes that are mocked by PowerMockRunner that make
55       *                                  private c-tors as public
56       * @return true if constructor is expected.
57       * @noinspection BooleanParameter
58       */
59      public static boolean isUtilsClassHasPrivateConstructor(final Class<?> utilClass,
60                                                               boolean checkConstructorIsPrivate)
61              throws ReflectiveOperationException {
62          final Constructor<?> constructor = utilClass.getDeclaredConstructor();
63          final boolean result;
64          if (checkConstructorIsPrivate && !Modifier.isPrivate(constructor.getModifiers())) {
65              result = false;
66          }
67          else {
68              constructor.setAccessible(true);
69              constructor.newInstance();
70              result = true;
71          }
72          return result;
73      }
74  
75      /**
76       * Retrieves the specified field by it's name in the class or it's direct super.
77       *
78       * @param clss The class to retrieve the field for.
79       * @param fieldName The name of the field to retrieve.
80       * @return The class' field.
81       * @throws NoSuchFieldException if the requested field cannot be found in the class.
82       */
83      public static Field getClassDeclaredField(Class<?> clss, String fieldName)
84              throws NoSuchFieldException {
85          final Optional<Field> classField = Arrays.stream(clss.getDeclaredFields())
86                  .filter(field -> fieldName.equals(field.getName())).findFirst();
87          final Field resultField;
88          if (classField.isPresent()) {
89              resultField = classField.get();
90          }
91          else {
92              resultField = clss.getSuperclass().getDeclaredField(fieldName);
93          }
94          resultField.setAccessible(true);
95          return resultField;
96      }
97  
98      /**
99       * Retrieves the specified method by it's name in the class or it's direct super.
100      *
101      * @param clss The class to retrieve the field for.
102      * @param methodName The name of the method to retrieve.
103      * @return The class' field.
104      * @throws NoSuchMethodException if the requested method cannot be found in the class.
105      */
106     public static Method getClassDeclaredMethod(Class<?> clss, String methodName)
107             throws NoSuchMethodException {
108         final Optional<Method> classMethod = Arrays.stream(clss.getDeclaredMethods())
109                 .filter(method -> methodName.equals(method.getName())).findFirst();
110         final Method resultMethod;
111         if (classMethod.isPresent()) {
112             resultMethod = classMethod.get();
113         }
114         else {
115             resultMethod = clss.getSuperclass().getDeclaredMethod(methodName);
116         }
117         resultMethod.setAccessible(true);
118         return resultMethod;
119     }
120 
121     /**
122      * Checks if stateful field is cleared during {@link AbstractCheck#beginTree} in check.
123      *
124      * @param check      check object which field is to be verified
125      * @param astToVisit ast to pass into check methods
126      * @param fieldName  name of the field to be checked
127      * @param isClear    function for checking field state
128      * @return {@code true} if state of the field is cleared
129      * @throws NoSuchFieldException   if there is no field with the
130      *                                {@code fieldName} in the {@code check}
131      * @throws IllegalAccessException if the field is inaccessible
132      */
133     public static boolean isStatefulFieldClearedDuringBeginTree(AbstractCheck check,
134                                                                 DetailAST astToVisit,
135                                                                 String fieldName,
136                                                                 Predicate<Object> isClear)
137             throws NoSuchFieldException, IllegalAccessException {
138         check.beginTree(astToVisit);
139         check.visitToken(astToVisit);
140         check.beginTree(null);
141         final Field resultField = getClassDeclaredField(check.getClass(), fieldName);
142         return isClear.test(resultField.get(check));
143     }
144 
145     /**
146      * Returns the default PackageObjectFactory with the default package names.
147      * @return the default PackageObjectFactory.
148      */
149     public static PackageObjectFactory getPackageObjectFactory() throws CheckstyleException {
150         final ClassLoader cl = TestUtil.class.getClassLoader();
151         final Set<String> packageNames = PackageNamesLoader.getPackageNames(cl);
152         return new PackageObjectFactory(packageNames, cl);
153     }
154 
155     /**
156      * Finds node of specified type among root children, siblings, siblings children
157      * on any deep level.
158      * @param root      DetailAST
159      * @param predicate predicate
160      * @return {@link Optional} of {@link DetailAST} node which matches the predicate.
161      */
162     public static Optional<DetailAST> findTokenInAstByPredicate(DetailAST root,
163                                                                 Predicate<DetailAST> predicate) {
164         DetailAST curNode = root;
165         while (!predicate.test(curNode)) {
166             DetailAST toVisit = curNode.getFirstChild();
167             while (curNode != null && toVisit == null) {
168                 toVisit = curNode.getNextSibling();
169                 if (toVisit == null) {
170                     curNode = curNode.getParent();
171                 }
172             }
173 
174             if (curNode == toVisit || curNode == root.getParent()) {
175                 curNode = null;
176                 break;
177             }
178 
179             curNode = toVisit;
180         }
181         return Optional.ofNullable(curNode);
182     }
183 
184     /**
185      * Parses Java source file. Results in AST which contains comment nodes.
186      * @param file file to parse
187      * @return DetailAST tree
188      * @throws NullPointerException if the text is null
189      * @throws IOException          if the file could not be read
190      * @throws ANTLRException       if parser or lexer failed
191      */
192     public static DetailAST parseFile(File file) throws IOException, ANTLRException {
193         final FileText text = new FileText(file.getAbsoluteFile(), StandardCharsets.UTF_8.name());
194         final FileContents contents = new FileContents(text);
195         return parseWithComments(contents);
196     }
197 }