diff --git a/pom.xml b/pom.xml index 763bd26..d5afcf3 100644 --- a/pom.xml +++ b/pom.xml @@ -286,9 +286,9 @@ 0 - com.github.checkstyle.regression.data.ModuleInfo - 0 - 0 + com.github.checkstyle.regression.module.ModuleUtils + 28 + 52 diff --git a/src/main/java/com/github/checkstyle/regression/module/ModuleCollector.java b/src/main/java/com/github/checkstyle/regression/module/ModuleCollector.java new file mode 100644 index 0000000..673e036 --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/module/ModuleCollector.java @@ -0,0 +1,63 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.github.checkstyle.regression.module; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.github.checkstyle.regression.data.GitChange; +import com.github.checkstyle.regression.data.ModifiableModuleInfo; +import com.github.checkstyle.regression.data.ModuleExtractInfo; +import com.github.checkstyle.regression.data.ModuleInfo; + +/** + * Collects all the necessary information for the generation, in module level. + * @author LuoLiangchen + */ +public final class ModuleCollector { + /** Prevents instantiation. */ + private ModuleCollector() { + } + + /** + * Generates the module information from a list of changes. + * @param changes the changes source + * @return the module information generated from the given changes + */ + public static List generate(List changes) { + final Map moduleInfos = new LinkedHashMap<>(); + + for (GitChange change : changes) { + if (ModuleUtils.isCheckstyleModule(change)) { + final String fullName = ModuleUtils.convertJavaSourceChangeToFullName(change); + final ModuleExtractInfo extractInfo = ModuleUtils.getModuleExtractInfo(fullName); + final ModifiableModuleInfo moduleInfo = ModifiableModuleInfo.create() + .setModuleExtractInfo(extractInfo); + moduleInfos.put(extractInfo.fullName(), moduleInfo); + } + } + + return moduleInfos.values().stream() + .map(ModifiableModuleInfo::toImmutable) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/github/checkstyle/regression/module/ModuleUtils.java b/src/main/java/com/github/checkstyle/regression/module/ModuleUtils.java new file mode 100644 index 0000000..80ee111 --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/module/ModuleUtils.java @@ -0,0 +1,191 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.github.checkstyle.regression.module; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import com.github.checkstyle.regression.data.GitChange; +import com.github.checkstyle.regression.data.ModuleExtractInfo; + +/** + * Contains utility methods related to checkstyle module. + * @author LuoLiangchen + */ +public final class ModuleUtils { + /** The compiled regex pattern of the path of Java source files. */ + private static final Pattern JAVA_SOURCE_PARTTEN = + Pattern.compile("src/(main|test)/java/(.+)\\.java"); + + /** The postfix of a test of a checkstyle module. */ + private static final String TEST_POSTFIX = "Test"; + + /** The map of full qualified name to module extract info. */ + private static final Map NAME_TO_MODULE_EXTRACT_INFO = + new HashMap<>(); + + /** Prevents instantiation. */ + private ModuleUtils() { + } + + /** + * Sets the map of full qualified name to module extract info with the given map. + * @param map the given map + */ + public static void setNameToModuleExtractInfo(Map map) { + NAME_TO_MODULE_EXTRACT_INFO.clear(); + NAME_TO_MODULE_EXTRACT_INFO.putAll(map); + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a checkstyle module. + * Changes of checkstyle modules are Java main source files, of which full + * qualified names are in the extract module info map. + * @param change change to check + * @return true if the corresponding file of a change may be considered as + * a checkstyle module + */ + public static boolean isCheckstyleModule(GitChange change) { + final boolean returnValue; + if (isJavaMainSource(change)) { + final String fullName = convertJavaSourceChangeToFullName(change); + returnValue = NAME_TO_MODULE_EXTRACT_INFO.containsKey(fullName); + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a checkstyle utility class. + * Changes of checkstyle utility classes are Java main source files, of which full + * qualified names are not in the extract module info map. + * @param change change to check + * @return true if the corresponding file of a change may be considered as + * a checkstyle utility class. + */ + public static boolean isCheckstyleUtility(GitChange change) { + final boolean returnValue; + if (isJavaMainSource(change)) { + final String fullName = convertJavaSourceChangeToFullName(change); + returnValue = !NAME_TO_MODULE_EXTRACT_INFO.containsKey(fullName); + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a test of checkstyle module. + * Changes of checkstyle module tests are Java test source files, of which full + * qualified names end with "Test" and the full names of corresponding modules are + * in the extract module info map. + * @param change change to check + * @return true if the corresponding file of a change may be considered as + * a test of checkstyle module + */ + public static boolean isCheckstyleModuleTest(GitChange change) { + final boolean returnValue; + if (isJavaTestSource(change)) { + final String fullName = convertJavaSourceChangeToFullName(change); + if (fullName.endsWith(TEST_POSTFIX)) { + returnValue = NAME_TO_MODULE_EXTRACT_INFO.containsKey( + fullName.substring(0, fullName.length() - TEST_POSTFIX.length())); + } + else { + returnValue = false; + } + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a Java main source file. + * Changes of Java main source files are which have path matching + * {@code JAVA_SOURCE_PARTTEN} and in "main" directory. + * @param change change to check + * @return true if the corresponding file of a change may be considered as + * a Java main source file. + */ + private static boolean isJavaMainSource(GitChange change) { + final boolean returnValue; + final Matcher matcher = JAVA_SOURCE_PARTTEN.matcher(change.getPath()); + if (matcher.find()) { + returnValue = "main".equals(matcher.group(1)); + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a Java test source file. + * Changes of Java test source files are which have path matching + * {@code JAVA_SOURCE_PARTTEN} and in "test" directory. + * @param change change to check + * @return true if the corresponding file of a change may be considered as + * a Java test source file. + */ + private static boolean isJavaTestSource(GitChange change) { + final boolean returnValue; + final Matcher matcher = JAVA_SOURCE_PARTTEN.matcher(change.getPath()); + if (matcher.find()) { + returnValue = "test".equals(matcher.group(1)); + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Gets the module extract info from the given full qualified name. + * @param fullName the given full qualified name + * @return the module extract info got from the given full qualified name + */ + public static ModuleExtractInfo getModuleExtractInfo(String fullName) { + return NAME_TO_MODULE_EXTRACT_INFO.get(fullName); + } + + /** + * Converts a change of Java source file to its full qualified name. + * @param change the change instance of Java source file + * @return the corresponding full qualified name + */ + public static String convertJavaSourceChangeToFullName(GitChange change) { + return Arrays.stream(JAVA_SOURCE_PARTTEN.matcher(change.getPath()) + .replaceAll("$2").split("/")).collect(Collectors.joining(".")); + } +} diff --git a/src/main/java/com/github/checkstyle/regression/module/package-info.java b/src/main/java/com/github/checkstyle/regression/module/package-info.java new file mode 100644 index 0000000..cf35f8f --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/module/package-info.java @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 +//////////////////////////////////////////////////////////////////////////////// + +/** + * Contains the module information generation and utility classes. + */ +package com.github.checkstyle.regression.module; diff --git a/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java b/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java new file mode 100644 index 0000000..024122c --- /dev/null +++ b/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java @@ -0,0 +1,129 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.github.checkstyle.regression.module; + +import static com.github.checkstyle.regression.internal.TestUtils.assertUtilsClassHasPrivateConstructor; +import static org.junit.Assert.assertEquals; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.github.checkstyle.regression.data.GitChange; +import com.github.checkstyle.regression.data.ImmutableModuleExtractInfo; +import com.github.checkstyle.regression.data.ImmutableModuleInfo; +import com.github.checkstyle.regression.data.ModuleExtractInfo; +import com.github.checkstyle.regression.data.ModuleInfo; +import com.github.checkstyle.regression.extract.ExtractInfoProcessor; + +public class ModuleInfoCollectorTest { + private static final String BASE_PACKAGE = + "com.puppycrawl.tools.checkstyle"; + + private static final String JAVA_MAIN_SOURCE_PREFIX = + "src/main/java/com/puppycrawl/tools/checkstyle/"; + + private static final String JAVA_TEST_SOURCE_PREFIX = + "src/test/java/com/puppycrawl/tools/checkstyle/"; + + @Before + public void setUp() { + final InputStream is = ExtractInfoProcessor.class.getClassLoader() + .getResourceAsStream("checkstyle_modules.json"); + final InputStreamReader reader = new InputStreamReader(is, Charset.forName("UTF-8")); + final Map map = + ExtractInfoProcessor.getModuleExtractInfosFromReader(reader); + ModuleUtils.setNameToModuleExtractInfo(map); + } + + @Test + public void testIsProperUtilsClass() throws Exception { + assertUtilsClassHasPrivateConstructor(ModuleCollector.class); + } + + @Test + public void testGenerateConfigNodesForValidChanges1() { + final GitChange moduleChange = new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "checks/coding/EmptyStatementCheck.java"); + final GitChange testChange = new GitChange( + JAVA_TEST_SOURCE_PREFIX + "checks/coding/EmptyStatementCheckTest.java"); + final GitChange nonRelatedChange = new GitChange( + JAVA_TEST_SOURCE_PREFIX + "checks/NewlineAtEndOfFileCheckTest.java"); + final List changes = Arrays.asList(moduleChange, testChange, nonRelatedChange); + final ModuleExtractInfo moduleExtractInfo = ImmutableModuleExtractInfo.builder() + .name("EmptyStatementCheck") + .packageName(BASE_PACKAGE + ".checks.coding") + .parent("TreeWalker") + .build(); + final List moduleInfos = + ModuleCollector.generate(changes); + final ModuleInfo moduleInfo = ImmutableModuleInfo.builder() + .moduleExtractInfo(moduleExtractInfo) + .build(); + assertEquals("The size of the module info list should be 1", 1, moduleInfos.size()); + assertEquals("The module info of EmptyStatementCheck is wrong", + moduleInfo, moduleInfos.get(0)); + // just for codecov, no need to check this. + assertEquals("The module name is wrong", "EmptyStatementCheck", moduleInfos.get(0).name()); + } + + @Test + public void testGenerateConfigNodesForValidChanges2() { + final GitChange moduleChange = new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "checks/NewlineAtEndOfFileCheck.java"); + final GitChange testChange = new GitChange( + JAVA_TEST_SOURCE_PREFIX + "checks/NewlineAtEndOfFileCheckTest.java"); + final GitChange nonRelatedChange = new GitChange( + JAVA_TEST_SOURCE_PREFIX + "checks/coding/EmptyStatementCheckTest.java"); + final List changes = Arrays.asList(moduleChange, testChange, nonRelatedChange); + final ModuleExtractInfo moduleExtractInfo = ImmutableModuleExtractInfo.builder() + .name("NewlineAtEndOfFileCheck") + .packageName(BASE_PACKAGE + ".checks") + .parent("Checker") + .build(); + final List moduleInfos = + ModuleCollector.generate(changes); + final ModuleInfo moduleInfo = ImmutableModuleInfo.builder() + .moduleExtractInfo(moduleExtractInfo) + .build(); + assertEquals("The size of the module info list should be 1", 1, moduleInfos.size()); + assertEquals("The module info of NewlineAtEndOfFileCheck is wrong", + moduleInfo, moduleInfos.get(0)); + } + + @Test + public void testGenerateConfigNodesForInvalidChanges() { + final List changes = new LinkedList<>(); + changes.add(new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "PackageObjectFactory.java")); + changes.add(new GitChange("src/main/java/Bar.java")); + changes.add(new GitChange("foo/A.java")); + final List moduleInfos = + ModuleCollector.generate(changes); + assertEquals("The size of the module info list should be 0", 0, moduleInfos.size()); + } +} diff --git a/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java b/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java new file mode 100644 index 0000000..423af6c --- /dev/null +++ b/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java @@ -0,0 +1,98 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.github.checkstyle.regression.module; + +import static com.github.checkstyle.regression.internal.TestUtils.assertUtilsClassHasPrivateConstructor; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +import com.github.checkstyle.regression.data.GitChange; +import com.github.checkstyle.regression.data.ImmutableModuleExtractInfo; +import com.github.checkstyle.regression.data.ModuleExtractInfo; +import com.github.checkstyle.regression.extract.ExtractInfoProcessor; + +public class ModuleUtilsTest { + private static final String BASE_PACKAGE = "com.puppycrawl.tools.checkstyle"; + + private static final String JAVA_MAIN_SOURCE_PREFIX = + "src/main/java/com/puppycrawl/tools/checkstyle/"; + + @Before + public void setUp() { + final InputStream is = ExtractInfoProcessor.class.getClassLoader() + .getResourceAsStream("checkstyle_modules.json"); + final InputStreamReader reader = new InputStreamReader(is, Charset.forName("UTF-8")); + final Map map = + ExtractInfoProcessor.getModuleExtractInfosFromReader(reader); + ModuleUtils.setNameToModuleExtractInfo(map); + } + + @Test + public void testIsProperUtilsClass() throws Exception { + assertUtilsClassHasPrivateConstructor(ModuleUtils.class); + } + + @Test + public void testIsCheckstyleModule() { + final GitChange change = new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "checks/coding/EmptyStatementCheck.java"); + final boolean result = ModuleUtils.isCheckstyleModule(change); + assertTrue("EmptyStatementCheck should be considered as a checkstyle module", result); + } + + @Test + public void testIsCheckstyleModuleNonModule() { + final GitChange change = new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "PackageObjectFactory.java"); + final boolean result = ModuleUtils.isCheckstyleModule(change); + assertFalse("PackageObjectFactory should not be considered as a checkstyle module", result); + } + + @Test + public void testIsCheckstyleModuleNonMainFile() { + final boolean result = + ModuleUtils.isCheckstyleModule(new GitChange("src/test/java/foo/Foo.java")); + assertFalse("Non main file should not be consideres as a checkstyle module", result); + } + + @Test + public void testConvertModuleChangeToExtractInfo() { + final GitChange change = new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "checks/coding/EmptyStatementCheck.java"); + final ModuleExtractInfo moduleExtractInfo = ModuleUtils + .getModuleExtractInfo(BASE_PACKAGE + ".checks.coding.EmptyStatementCheck"); + final ModuleExtractInfo expected = ImmutableModuleExtractInfo.builder() + .name("EmptyStatementCheck") + .packageName(BASE_PACKAGE + ".checks.coding") + .parent("TreeWalker") + .build(); + assertEquals("The extract info of EmptyStatementCheck is wrong", + expected, moduleExtractInfo); + } +}