diff --git a/checkstyle-sonar-plugin/pom.xml b/checkstyle-sonar-plugin/pom.xml index b496c4eb..fcb103ee 100644 --- a/checkstyle-sonar-plugin/pom.xml +++ b/checkstyle-sonar-plugin/pom.xml @@ -94,6 +94,13 @@ test + + org.apache.ant + ant + 1.9.7 + test + + diff --git a/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/CheckUtil.java b/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/CheckUtil.java index 7c1befcd..ad7a6013 100644 --- a/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/CheckUtil.java +++ b/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/CheckUtil.java @@ -22,40 +22,103 @@ import java.io.IOException; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.text.MessageFormat; import java.util.HashSet; import java.util.Properties; import java.util.Set; +import com.puppycrawl.tools.checkstyle.api.AbstractCheck; +import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; +import com.puppycrawl.tools.checkstyle.api.AutomaticBean; +import com.puppycrawl.tools.checkstyle.api.Filter; +import com.puppycrawl.tools.checkstyle.guava.collect.ImmutableSet; +import com.puppycrawl.tools.checkstyle.guava.reflect.ClassPath; +import com.puppycrawl.tools.checkstyle.guava.reflect.ClassPath.ClassInfo; + public final class CheckUtil { private CheckUtil() { } /** - * Gets the checkstyle's modules. Checkstyle's modules are nonabstract - * classes from com.puppycrawl.tools.checkstyle package which names end with - * 'Check', do not contain the word 'Input' (are not input files for UTs), - * checkstyle's filters and SuppressWarningsHolder class. - * - * @return a set of checkstyle's modules names. + * Gets all checkstyle's modules. + * @return the set of checkstyle's module classes. * @throws IOException if the attempt to read class path resources failed. + * @see #isCheckstyleModule(Class) */ public static Set> getCheckstyleModules() throws IOException { final Set> checkstyleModules = new HashSet<>(); -// final ClassLoader loader = Thread.currentThread().getContextClassLoader(); -// final ClassPath classpath = ClassPath.from(loader); -// final String packageName = "com.puppycrawl.tools.checkstyle.checks"; -// final ImmutableSet checkstyleClasses = classpath -// .getTopLevelClassesRecursive(packageName); -// -// for (ClassPath.ClassInfo clazz : checkstyleClasses) { -// final Class loadedClass = clazz.load(); -// if (isCheckstyleModule(loadedClass)) { -// checkstyleModules.add(loadedClass); -// } -// } + final ClassLoader loader = Thread.currentThread() + .getContextClassLoader(); + final ClassPath classpath = ClassPath.from(loader); + final String packageName = "com.puppycrawl.tools.checkstyle"; + final ImmutableSet checkstyleClasses = classpath + .getTopLevelClassesRecursive(packageName); + + for (ClassPath.ClassInfo clazz : checkstyleClasses) { + final Class loadedClass = clazz.load(); + if (isCheckstyleModule(loadedClass)) { + checkstyleModules.add(loadedClass); + } + } return checkstyleModules; + } /** + * Checks whether a class may be considered as a checkstyle module. Checkstyle's modules are + * non-abstract classes, which names do not start with the word 'Input' (are not input files for + * UTs), and are either checkstyle's checks, file sets, filters, file filters, or root module. + * @param loadedClass class to check. + * @return true if the class may be considered as the checkstyle module. + */ + private static boolean isCheckstyleModule(Class loadedClass) { + final String className = loadedClass.getSimpleName(); + return isValidCheckstyleClass(loadedClass, className) + && (isCheckstyleCheck(loadedClass) + || isFileSetModule(loadedClass) + || isFilterModule(loadedClass)); + } + + /** + * Checks whether a class extends 'AutomaticBean', is non-abstract, and doesn't start with the + * word 'Input' (are not input files for UTs). + * @param loadedClass class to check. + * @param className class name to check. + * @return true if a class may be considered a valid production class. + */ + public static boolean isValidCheckstyleClass(Class loadedClass, String className) { + return AutomaticBean.class.isAssignableFrom(loadedClass) + && !Modifier.isAbstract(loadedClass.getModifiers()) + && !className.contains("Input"); + } + + /** + * Checks whether a class may be considered as the checkstyle check. + * Checkstyle's checks are classes which implement 'AbstractCheck' interface. + * @param loadedClass class to check. + * @return true if a class may be considered as the checkstyle check. + */ + public static boolean isCheckstyleCheck(Class loadedClass) { + return AbstractCheck.class.isAssignableFrom(loadedClass); + } + + /** + * Checks whether a class may be considered as the checkstyle file set. + * Checkstyle's file sets are classes which implement 'AbstractFileSetCheck' interface. + * @param loadedClass class to check. + * @return true if a class may be considered as the checkstyle file set. + */ + public static boolean isFileSetModule(Class loadedClass) { + return AbstractFileSetCheck.class.isAssignableFrom(loadedClass); + } + + /** + * Checks whether a class may be considered as the checkstyle filter. + * Checkstyle's filters are classes which implement 'Filter' interface. + * @param loadedClass class to check. + * @return true if a class may be considered as the checkstyle filter. + */ + public static boolean isFilterModule(Class loadedClass) { + return Filter.class.isAssignableFrom(loadedClass); } /** diff --git a/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/ChecksTest.java b/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/ChecksTest.java index e649d7f1..7863ff8e 100644 --- a/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/ChecksTest.java +++ b/checkstyle-sonar-plugin/src/test/java/org/sonar/plugins/checkstyle/internal/ChecksTest.java @@ -33,13 +33,13 @@ import org.apache.commons.beanutils.PropertyUtils; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import com.puppycrawl.tools.checkstyle.TreeWalker; import com.puppycrawl.tools.checkstyle.api.AbstractCheck; import com.puppycrawl.tools.checkstyle.api.AbstractFileSetCheck; import com.puppycrawl.tools.checkstyle.checks.javadoc.AbstractJavadocCheck; @@ -65,7 +65,6 @@ public final class ChecksTest { ); @SuppressWarnings("static-method") - @Ignore @Test public void verifyTestConfigurationFiles() throws Exception { final Set> modules = CheckUtil.getCheckstyleModules(); @@ -95,6 +94,10 @@ private static void validateSonarFile(Document document, Set> modules) final String key = rule.getAttributes().getNamedItem("key").getTextContent(); final Class module = findModule(modules, key); + + if (CheckUtil.isFilterModule(module)) + Assert.fail("Module should not be in sonar rules: " + module.getCanonicalName()); + modules.remove(module); Assert.assertNotNull("Unknown class found in sonar: " + key, module); @@ -114,7 +117,7 @@ private static void validateSonarFile(Document document, Set> modules) expectedConfigKey = "Checker/TreeWalker/" + moduleSimpleName.replaceAll("Check$", ""); } else { - expectedConfigKey = "Checker/" + moduleSimpleName.replaceAll("Check$", "");; + expectedConfigKey = "Checker/" + moduleSimpleName.replaceAll("Check$", ""); } Assert.assertNotNull(moduleName + " requires a configKey in sonar", configKey); @@ -125,7 +128,8 @@ private static void validateSonarFile(Document document, Set> modules) } for (Class module : modules) { - Assert.fail("Module not found in sonar: " + module.getCanonicalName()); + if (!CheckUtil.isFilterModule(module) && module != TreeWalker.class) + Assert.fail("Module not found in sonar: " + module.getCanonicalName()); } }