diff --git a/pom.xml b/pom.xml index 763bd26..aff7011 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 + 33 + 57 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..f41949b --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/module/ModuleCollector.java @@ -0,0 +1,62 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 ModuleExtractInfo extractInfo = + ModuleUtils.convertModuleChangeToExtractInfo(change); + 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..d6a78a9 --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/module/ModuleUtils.java @@ -0,0 +1,172 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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_EXTRAT_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 setNameToModuleExtratInfo(Map map) { + NAME_TO_MODULE_EXTRAT_INFO.clear(); + NAME_TO_MODULE_EXTRAT_INFO.putAll(map); + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a checkstyle module. + * @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 = convertJavaSourcePathToFullName(change.getPath()); + returnValue = NAME_TO_MODULE_EXTRAT_INFO.containsKey(fullName); + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a checkstyle utility class. + * @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 = convertJavaSourcePathToFullName(change.getPath()); + returnValue = !NAME_TO_MODULE_EXTRAT_INFO.containsKey(fullName); + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Checks whether the corresponding file of a change may be considered as + * a Java main source file. + * @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 test of checkstyle module. + * @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 (JAVA_SOURCE_PARTTEN.matcher(change.getPath()).find()) { + final String fullName = convertJavaSourcePathToFullName(change.getPath()); + if (fullName.endsWith(TEST_POSTFIX)) { + returnValue = NAME_TO_MODULE_EXTRAT_INFO.containsKey( + fullName.substring(0, fullName.length() - TEST_POSTFIX.length())); + } + else { + returnValue = false; + } + } + else { + returnValue = false; + } + return returnValue; + } + + /** + * Converts a {@link GitChange} of checkstyle module to a {@link ModuleExtractInfo}. + * @param change the change to convert + * @return the module info converted from the given change + */ + public static ModuleExtractInfo convertModuleChangeToExtractInfo(GitChange change) { + final String fullName = convertJavaSourcePathToFullName(change.getPath()); + return NAME_TO_MODULE_EXTRAT_INFO.get(fullName); + } + + /** + * Converts a {@link GitChange} of test to the full name of corresponding checkstyle module. + * @param change the change to convert + * @return the full name of corresponding checkstyle module + */ + public static String convertModuleTestChangeToModuleFullName(GitChange change) { + final String testName = convertJavaSourcePathToFullName(change.getPath()); + return testName.substring(0, testName.length() - TEST_POSTFIX.length()); + } + + /** + * Converts a path of Java source file to its full qualified name. + * @param path the path of Java source file + * @return the corresponding full qualified name + */ + private static String convertJavaSourcePathToFullName(String path) { + return Arrays.stream(JAVA_SOURCE_PARTTEN.matcher(path).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..9e117b2 --- /dev/null +++ b/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java @@ -0,0 +1,127 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.setNameToModuleExtratInfo(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(1, moduleInfos.size()); + assertEquals(moduleInfo, moduleInfos.get(0)); + // just for codecov, no need to check this. + assertEquals("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(1, moduleInfos.size()); + assertEquals(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(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..90c3560 --- /dev/null +++ b/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java @@ -0,0 +1,97 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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.setNameToModuleExtratInfo(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(result); + } + + @Test + public void testIsCheckstyleModuleNonModule() { + final GitChange change = new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "PackageObjectFactory.java"); + final boolean result = ModuleUtils.isCheckstyleModule(change); + assertFalse(result); + } + + @Test + public void testIsCheckstyleModuleNonMainFile() { + final boolean result = + ModuleUtils.isCheckstyleModule(new GitChange("src/test/java/foo/Foo.java")); + assertFalse(result); + } + + @Test + public void testConvertModuleChangeToModuleInfo() { + final GitChange change = new GitChange( + JAVA_MAIN_SOURCE_PREFIX + "checks/coding/EmptyStatementCheck.java"); + final ModuleExtractInfo moduleExtractInfo = + ModuleUtils.convertModuleChangeToExtractInfo(change); + final ModuleExtractInfo expected = ImmutableModuleExtractInfo.builder() + .name("EmptyStatementCheck") + .packageName(BASE_PACKAGE + ".checks.coding") + .parent("TreeWalker") + .build(); + assertEquals(expected, moduleExtractInfo); + } +}