blob: df85e9aad5662c5e870297d8c1a37b737b8bd07c [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
// checkstyle: Checks Java source code for adherence to a set of rules.
// Copyright (C) 2001-2017 the original author or authors.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
////////////////////////////////////////////////////////////////////////////////
package com.puppycrawl.tools.checkstyle.internal.utils;
import static com.puppycrawl.tools.checkstyle.TreeWalker.parseWithComments;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import antlr.ANTLRException;
import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
import com.puppycrawl.tools.checkstyle.PackageObjectFactory;
import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.DetailAST;
import com.puppycrawl.tools.checkstyle.api.FileContents;
import com.puppycrawl.tools.checkstyle.api.FileText;
public final class TestUtil {
private TestUtil() {
}
/**
* Verifies that utils class has private constructor and invokes it to satisfy code coverage.
* @param utilClass class to test for c-tor
* @param checkConstructorIsPrivate flag to skip check for private visibility, it is useful
* for Classes that are mocked by PowerMockRunner that make
* private c-tors as public
* @return true if constructor is expected.
* @noinspection BooleanParameter
*/
public static boolean isUtilsClassHasPrivateConstructor(final Class<?> utilClass,
boolean checkConstructorIsPrivate)
throws ReflectiveOperationException {
final Constructor<?> constructor = utilClass.getDeclaredConstructor();
final boolean result;
if (checkConstructorIsPrivate && !Modifier.isPrivate(constructor.getModifiers())) {
result = false;
}
else {
constructor.setAccessible(true);
constructor.newInstance();
result = true;
}
return result;
}
/**
* Retrieves the specified field by it's name in the class or it's direct super.
*
* @param clss The class to retrieve the field for.
* @param fieldName The name of the field to retrieve.
* @return The class' field.
* @throws NoSuchFieldException if the requested field cannot be found in the class.
*/
public static Field getClassDeclaredField(Class<?> clss, String fieldName)
throws NoSuchFieldException {
final Optional<Field> classField = Arrays.stream(clss.getDeclaredFields())
.filter(field -> fieldName.equals(field.getName())).findFirst();
final Field resultField;
if (classField.isPresent()) {
resultField = classField.get();
}
else {
resultField = clss.getSuperclass().getDeclaredField(fieldName);
}
resultField.setAccessible(true);
return resultField;
}
/**
* Retrieves the specified method by it's name in the class or it's direct super.
*
* @param clss The class to retrieve the field for.
* @param methodName The name of the method to retrieve.
* @return The class' field.
* @throws NoSuchMethodException if the requested method cannot be found in the class.
*/
public static Method getClassDeclaredMethod(Class<?> clss, String methodName)
throws NoSuchMethodException {
final Optional<Method> classMethod = Arrays.stream(clss.getDeclaredMethods())
.filter(method -> methodName.equals(method.getName())).findFirst();
final Method resultMethod;
if (classMethod.isPresent()) {
resultMethod = classMethod.get();
}
else {
resultMethod = clss.getSuperclass().getDeclaredMethod(methodName);
}
resultMethod.setAccessible(true);
return resultMethod;
}
/**
* Checks if stateful field is cleared during {@link AbstractCheck#beginTree} in check.
*
* @param check check object which field is to be verified
* @param astToVisit ast to pass into check methods
* @param fieldName name of the field to be checked
* @param isClear function for checking field state
* @return {@code true} if state of the field is cleared
* @throws NoSuchFieldException if there is no field with the
* {@code fieldName} in the {@code check}
* @throws IllegalAccessException if the field is inaccessible
*/
public static boolean isStatefulFieldClearedDuringBeginTree(AbstractCheck check,
DetailAST astToVisit,
String fieldName,
Predicate<Object> isClear)
throws NoSuchFieldException, IllegalAccessException {
check.beginTree(astToVisit);
check.visitToken(astToVisit);
check.beginTree(null);
final Field resultField = getClassDeclaredField(check.getClass(), fieldName);
return isClear.test(resultField.get(check));
}
/**
* Returns the default PackageObjectFactory with the default package names.
* @return the default PackageObjectFactory.
*/
public static PackageObjectFactory getPackageObjectFactory() throws CheckstyleException {
final ClassLoader cl = TestUtil.class.getClassLoader();
final Set<String> packageNames = PackageNamesLoader.getPackageNames(cl);
return new PackageObjectFactory(packageNames, cl);
}
/**
* Finds node of specified type among root children, siblings, siblings children
* on any deep level.
* @param root DetailAST
* @param predicate predicate
* @return {@link Optional} of {@link DetailAST} node which matches the predicate.
*/
public static Optional<DetailAST> findTokenInAstByPredicate(DetailAST root,
Predicate<DetailAST> predicate) {
DetailAST curNode = root;
while (!predicate.test(curNode)) {
DetailAST toVisit = curNode.getFirstChild();
while (curNode != null && toVisit == null) {
toVisit = curNode.getNextSibling();
if (toVisit == null) {
curNode = curNode.getParent();
}
}
if (curNode == toVisit || curNode == root.getParent()) {
curNode = null;
break;
}
curNode = toVisit;
}
return Optional.ofNullable(curNode);
}
/**
* Parses Java source file. Results in AST which contains comment nodes.
* @param file file to parse
* @return DetailAST tree
* @throws NullPointerException if the text is null
* @throws IOException if the file could not be read
* @throws ANTLRException if parser or lexer failed
*/
public static DetailAST parseFile(File file) throws IOException, ANTLRException {
final FileText text = new FileText(file.getAbsoluteFile(), StandardCharsets.UTF_8.name());
final FileContents contents = new FileContents(text);
return parseWithComments(contents);
}
}