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